From a34cc923276e875c444b128c2a7e359649c51ec0 Mon Sep 17 00:00:00 2001 From: ypsdf1 Date: Sun, 7 Jun 2026 18:40:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E7=89=A9=E5=93=81=E4=B8=8D=E7=94=9F=E6=95=88?= =?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=8C=E6=96=B0=E5=A2=9E=E9=98=BB=E6=AD=A2?= =?UTF-8?q?sdf1=5Fgame=E5=AE=9D=E7=89=A9=E6=8A=95=E5=85=A5=E5=9E=83?= =?UTF-8?q?=E5=9C=BE=E7=AE=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backup/20260605_152529/.gitattributes | 5 + backup/20260605_152529/.gitignore | 46 + .../AreaProtection.java.backup | 4907 ++++++++++++++ backup/20260605_152529/README.md | 569 ++ backup/20260605_152529/pom.xml | 66 + .../src/main/java/Sdf1_login/AFKManager.java | 197 + .../Sdf1_login/AccountRequestManager.java | 146 + .../main/java/Sdf1_login/AreaProtection.java | 4907 ++++++++++++++ .../src/main/java/Sdf1_login/BondManager.java | 697 ++ .../src/main/java/Sdf1_login/BondPrinter.java | 378 ++ .../src/main/java/Sdf1_login/CDKManager.java | 293 + .../java/Sdf1_login/ChatFilterManager.java | 612 ++ .../java/Sdf1_login/ChatInputManager.java | 842 +++ .../main/java/Sdf1_login/CheckInManager.java | 302 + .../java/Sdf1_login/CommissionManager.java | 227 + .../main/java/Sdf1_login/ConfigManager.java | 372 ++ .../main/java/Sdf1_login/CypayCommand.java | 577 ++ .../main/java/Sdf1_login/DatabaseManager.java | 1758 +++++ .../main/java/Sdf1_login/EmailManager.java | 284 + .../src/main/java/Sdf1_login/GUIManager.java | 2681 ++++++++ .../main/java/Sdf1_login/GarbageManager.java | 573 ++ .../src/main/java/Sdf1_login/GiftManager.java | 259 + .../main/java/Sdf1_login/IPGroupManager.java | 110 + .../java/Sdf1_login/InventorySerializer.java | 46 + .../main/java/Sdf1_login/InviteManager.java | 96 + .../java/Sdf1_login/IronBlockElevator.java | 95 + .../main/java/Sdf1_login/LoginManager.java | 311 + .../src/main/java/Sdf1_login/Main.java | 5565 ++++++++++++++++ .../main/java/Sdf1_login/MenuIconManager.java | 152 + .../src/main/java/Sdf1_login/MenuManager.java | 452 ++ .../main/java/Sdf1_login/OrderManager.java | 1345 ++++ .../src/main/java/Sdf1_login/PVPManager.java | 1380 ++++ .../main/java/Sdf1_login/PasswordUtils.java | 72 + .../main/java/Sdf1_login/PlayerStorage.java | 130 + .../main/java/Sdf1_login/PointsManager.java | 374 ++ .../main/java/Sdf1_login/QuestStorage.java | 138 + .../main/java/Sdf1_login/QuestTracker.java | 3662 +++++++++++ .../Sdf1_login/RadioDownloadListener.java | 144 + .../main/java/Sdf1_login/RadioManager.java | 1040 +++ .../main/java/Sdf1_login/RefundRecord.java | 62 + .../java/Sdf1_login/RiskControlManager.java | 248 + .../java/Sdf1_login/SalesStatsManager.java | 170 + .../main/java/Sdf1_login/ShopCategory.java | 32 + .../src/main/java/Sdf1_login/ShopItem.java | 49 + .../src/main/java/Sdf1_login/ShopManager.java | 2610 ++++++++ .../main/java/Sdf1_login/TicketManager.java | 1644 +++++ .../main/java/Sdf1_login/UpdateChecker.java | 523 ++ .../java/Sdf1_login/UserGroupManager.java | 370 ++ .../java/Sdf1_login/VerificationManager.java | 158 + .../main/java/Sdf1_login/WelcomeManager.java | 163 + .../src/main/resources/plugin.yml | 104 + .../CheckInManager.java.backup | 305 + .../DatabaseManager.java.backup | 1758 +++++ backup/20260605_185500/Main.java.backup | 5568 ++++++++++++++++ .../20260605_185500/QuestStorage.java.backup | 138 + .../20260605_185500/QuestTracker.java.backup | 3692 +++++++++++ backup/Main_backup_20260606_235342.java | 5734 +++++++++++++++++ backup/QuestTracker.java.backup | 3662 +++++++++++ backup/README.md | 569 ++ backup/img/cart1.png | Bin 0 -> 99972 bytes backup/img/login.png | Bin 0 -> 84601 bytes backup/img/login1.png | Bin 0 -> 108432 bytes backup/img/login10.png | Bin 0 -> 96574 bytes backup/img/login11.png | Bin 0 -> 89730 bytes backup/img/login12.png | Bin 0 -> 75156 bytes backup/img/login13.png | Bin 0 -> 65958 bytes backup/img/login14.png | Bin 0 -> 46201 bytes backup/img/login15.png | Bin 0 -> 77597 bytes backup/img/login2.png | Bin 0 -> 73963 bytes backup/img/login3.png | Bin 0 -> 139266 bytes backup/img/login4.png | Bin 0 -> 170338 bytes backup/img/login5.png | Bin 0 -> 198550 bytes backup/img/login6.png | Bin 0 -> 78484 bytes backup/img/login7.png | Bin 0 -> 103162 bytes backup/img/login8.png | Bin 0 -> 46680 bytes backup/img/login9.png | Bin 0 -> 115847 bytes backup/img/menu1.png | Bin 0 -> 208287 bytes backup/img/menu2.png | Bin 0 -> 158626 bytes backup/img/pvp1.png | Bin 0 -> 155861 bytes backup/img/pvp3.png | Bin 0 -> 76299 bytes backup/img/shop1.png | Bin 0 -> 97464 bytes backup/img/shop2.png | Bin 0 -> 179010 bytes backup/img/shop4.png | Bin 0 -> 211910 bytes backup/img/shop5.png | Bin 0 -> 99041 bytes backup/img/shop6.png | Bin 0 -> 230212 bytes backup/img/shop7.png | Bin 0 -> 111967 bytes backup/pom.xml | 66 + .../src/main/java/Sdf1_login/AFKManager.java | 197 + .../Sdf1_login/AccountRequestManager.java | 146 + .../main/java/Sdf1_login/AreaProtection.java | 4907 ++++++++++++++ .../src/main/java/Sdf1_login/BondManager.java | 697 ++ .../src/main/java/Sdf1_login/BondPrinter.java | 378 ++ .../src/main/java/Sdf1_login/CDKManager.java | 293 + .../java/Sdf1_login/ChatFilterManager.java | 612 ++ .../java/Sdf1_login/ChatInputManager.java | 842 +++ .../main/java/Sdf1_login/CheckInManager.java | 302 + .../java/Sdf1_login/CommissionManager.java | 227 + .../main/java/Sdf1_login/ConfigManager.java | 372 ++ .../main/java/Sdf1_login/CypayCommand.java | 577 ++ .../main/java/Sdf1_login/DatabaseManager.java | 1758 +++++ .../main/java/Sdf1_login/EmailManager.java | 284 + .../src/main/java/Sdf1_login/GUIManager.java | 2681 ++++++++ .../main/java/Sdf1_login/GarbageManager.java | 573 ++ .../src/main/java/Sdf1_login/GiftManager.java | 259 + .../main/java/Sdf1_login/IPGroupManager.java | 110 + .../java/Sdf1_login/InventorySerializer.java | 46 + .../main/java/Sdf1_login/InviteManager.java | 96 + .../java/Sdf1_login/IronBlockElevator.java | 71 + .../main/java/Sdf1_login/LoginManager.java | 311 + backup/src/main/java/Sdf1_login/Main.java | 5565 ++++++++++++++++ .../main/java/Sdf1_login/MenuIconManager.java | 152 + .../src/main/java/Sdf1_login/MenuManager.java | 452 ++ .../main/java/Sdf1_login/OrderManager.java | 1345 ++++ .../src/main/java/Sdf1_login/PVPManager.java | 1380 ++++ .../main/java/Sdf1_login/PasswordUtils.java | 72 + .../main/java/Sdf1_login/PlayerStorage.java | 130 + .../main/java/Sdf1_login/PointsManager.java | 374 ++ .../main/java/Sdf1_login/QuestStorage.java | 138 + .../main/java/Sdf1_login/QuestTracker.java | 3252 ++++++++++ .../Sdf1_login/RadioDownloadListener.java | 144 + .../main/java/Sdf1_login/RadioManager.java | 1040 +++ .../main/java/Sdf1_login/RefundRecord.java | 62 + .../java/Sdf1_login/RiskControlManager.java | 248 + .../java/Sdf1_login/SalesStatsManager.java | 170 + .../main/java/Sdf1_login/ShopCategory.java | 32 + backup/src/main/java/Sdf1_login/ShopItem.java | 49 + .../src/main/java/Sdf1_login/ShopManager.java | 2610 ++++++++ .../main/java/Sdf1_login/TicketManager.java | 1644 +++++ .../main/java/Sdf1_login/UpdateChecker.java | 523 ++ .../java/Sdf1_login/UserGroupManager.java | 370 ++ .../java/Sdf1_login/VerificationManager.java | 158 + .../main/java/Sdf1_login/WelcomeManager.java | 163 + backup/src/main/resources/plugin.yml | 104 + .../main/java/Sdf1_login/AFKManager.java | 197 + .../Sdf1_login/AccountRequestManager.java | 146 + .../main/java/Sdf1_login/AreaProtection.java | 4907 ++++++++++++++ .../main/java/Sdf1_login/BondManager.java | 697 ++ .../main/java/Sdf1_login/BondPrinter.java | 378 ++ .../main/java/Sdf1_login/CDKManager.java | 293 + .../java/Sdf1_login/ChatFilterManager.java | 612 ++ .../java/Sdf1_login/ChatInputManager.java | 842 +++ .../main/java/Sdf1_login/CheckInManager.java | 302 + .../java/Sdf1_login/CommissionManager.java | 227 + .../main/java/Sdf1_login/ConfigManager.java | 372 ++ .../main/java/Sdf1_login/CypayCommand.java | 577 ++ .../main/java/Sdf1_login/DatabaseManager.java | 1758 +++++ .../main/java/Sdf1_login/EmailManager.java | 284 + .../main/java/Sdf1_login/GUIManager.java | 2681 ++++++++ .../main/java/Sdf1_login/GarbageManager.java | 573 ++ .../main/java/Sdf1_login/GiftManager.java | 259 + .../main/java/Sdf1_login/IPGroupManager.java | 110 + .../java/Sdf1_login/InventorySerializer.java | 46 + .../main/java/Sdf1_login/InviteManager.java | 96 + .../java/Sdf1_login/IronBlockElevator.java | 95 + .../main/java/Sdf1_login/LoginManager.java | 311 + .../main/java/Sdf1_login/Main.java | 5565 ++++++++++++++++ .../main/java/Sdf1_login/MenuIconManager.java | 152 + .../main/java/Sdf1_login/MenuManager.java | 452 ++ .../main/java/Sdf1_login/OrderManager.java | 1345 ++++ .../main/java/Sdf1_login/PVPManager.java | 1380 ++++ .../main/java/Sdf1_login/PasswordUtils.java | 72 + .../main/java/Sdf1_login/PlayerStorage.java | 130 + .../main/java/Sdf1_login/PointsManager.java | 374 ++ .../main/java/Sdf1_login/QuestStorage.java | 138 + .../main/java/Sdf1_login/QuestTracker.java | 3537 ++++++++++ .../Sdf1_login/RadioDownloadListener.java | 144 + .../main/java/Sdf1_login/RadioManager.java | 1040 +++ .../main/java/Sdf1_login/RefundRecord.java | 62 + .../java/Sdf1_login/RiskControlManager.java | 248 + .../java/Sdf1_login/SalesStatsManager.java | 170 + .../main/java/Sdf1_login/ShopCategory.java | 32 + .../main/java/Sdf1_login/ShopItem.java | 49 + .../main/java/Sdf1_login/ShopManager.java | 2610 ++++++++ .../main/java/Sdf1_login/TicketManager.java | 1644 +++++ .../main/java/Sdf1_login/UpdateChecker.java | 523 ++ .../java/Sdf1_login/UserGroupManager.java | 370 ++ .../java/Sdf1_login/VerificationManager.java | 158 + .../main/java/Sdf1_login/WelcomeManager.java | 163 + .../main/resources/plugin.yml | 104 + src/main/java/Sdf1_login/AreaProtection.java | 2862 +++++--- src/main/java/Sdf1_login/BondManager.java | 2 + src/main/java/Sdf1_login/BondPrinter.java | 2 + src/main/java/Sdf1_login/CDKManager.java | 41 + src/main/java/Sdf1_login/CheckInManager.java | 11 +- src/main/java/Sdf1_login/CypayCommand.java | 29 +- src/main/java/Sdf1_login/DatabaseManager.java | 8 + src/main/java/Sdf1_login/GUIManager.java | 446 +- .../java/Sdf1_login/IronBlockElevator.java | 95 + src/main/java/Sdf1_login/LoginManager.java | 15 + src/main/java/Sdf1_login/Main.java | 513 +- src/main/java/Sdf1_login/QuestStorage.java | 259 +- src/main/java/Sdf1_login/QuestTracker.java | 599 +- .../Sdf1_login/RadioDownloadListener.java | 13 +- src/main/java/Sdf1_login/ShopItem.java | 1 - src/main/java/Sdf1_login/ShopManager.java | 106 +- src/main/java/Sdf1_login/TicketManager.java | 105 +- src/main/java/Sdf1_login/TreasureBridge.java | 116 + src/main/java/Sdf1_login/UpdateChecker.java | 965 ++- src/main/resources/plugin.yml | 7 +- 199 files changed, 140045 insertions(+), 1726 deletions(-) create mode 100644 backup/20260605_152529/.gitattributes create mode 100644 backup/20260605_152529/.gitignore create mode 100644 backup/20260605_152529/AreaProtection.java.backup create mode 100644 backup/20260605_152529/README.md create mode 100644 backup/20260605_152529/pom.xml create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/AFKManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/AccountRequestManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/AreaProtection.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/BondManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/BondPrinter.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/CDKManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ChatFilterManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ChatInputManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/CheckInManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/CommissionManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ConfigManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/CypayCommand.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/DatabaseManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/EmailManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/GUIManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/GarbageManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/GiftManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/IPGroupManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/InventorySerializer.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/InviteManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/IronBlockElevator.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/LoginManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/Main.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/MenuIconManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/MenuManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/OrderManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/PVPManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/PasswordUtils.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/PlayerStorage.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/PointsManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/QuestStorage.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/QuestTracker.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/RadioDownloadListener.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/RadioManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/RefundRecord.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/RiskControlManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/SalesStatsManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ShopCategory.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ShopItem.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/ShopManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/TicketManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/UpdateChecker.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/UserGroupManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/VerificationManager.java create mode 100644 backup/20260605_152529/src/main/java/Sdf1_login/WelcomeManager.java create mode 100644 backup/20260605_152529/src/main/resources/plugin.yml create mode 100644 backup/20260605_185500/CheckInManager.java.backup create mode 100644 backup/20260605_185500/DatabaseManager.java.backup create mode 100644 backup/20260605_185500/Main.java.backup create mode 100644 backup/20260605_185500/QuestStorage.java.backup create mode 100644 backup/20260605_185500/QuestTracker.java.backup create mode 100644 backup/Main_backup_20260606_235342.java create mode 100644 backup/QuestTracker.java.backup create mode 100644 backup/README.md create mode 100644 backup/img/cart1.png create mode 100644 backup/img/login.png create mode 100644 backup/img/login1.png create mode 100644 backup/img/login10.png create mode 100644 backup/img/login11.png create mode 100644 backup/img/login12.png create mode 100644 backup/img/login13.png create mode 100644 backup/img/login14.png create mode 100644 backup/img/login15.png create mode 100644 backup/img/login2.png create mode 100644 backup/img/login3.png create mode 100644 backup/img/login4.png create mode 100644 backup/img/login5.png create mode 100644 backup/img/login6.png create mode 100644 backup/img/login7.png create mode 100644 backup/img/login8.png create mode 100644 backup/img/login9.png create mode 100644 backup/img/menu1.png create mode 100644 backup/img/menu2.png create mode 100644 backup/img/pvp1.png create mode 100644 backup/img/pvp3.png create mode 100644 backup/img/shop1.png create mode 100644 backup/img/shop2.png create mode 100644 backup/img/shop4.png create mode 100644 backup/img/shop5.png create mode 100644 backup/img/shop6.png create mode 100644 backup/img/shop7.png create mode 100644 backup/pom.xml create mode 100644 backup/src/main/java/Sdf1_login/AFKManager.java create mode 100644 backup/src/main/java/Sdf1_login/AccountRequestManager.java create mode 100644 backup/src/main/java/Sdf1_login/AreaProtection.java create mode 100644 backup/src/main/java/Sdf1_login/BondManager.java create mode 100644 backup/src/main/java/Sdf1_login/BondPrinter.java create mode 100644 backup/src/main/java/Sdf1_login/CDKManager.java create mode 100644 backup/src/main/java/Sdf1_login/ChatFilterManager.java create mode 100644 backup/src/main/java/Sdf1_login/ChatInputManager.java create mode 100644 backup/src/main/java/Sdf1_login/CheckInManager.java create mode 100644 backup/src/main/java/Sdf1_login/CommissionManager.java create mode 100644 backup/src/main/java/Sdf1_login/ConfigManager.java create mode 100644 backup/src/main/java/Sdf1_login/CypayCommand.java create mode 100644 backup/src/main/java/Sdf1_login/DatabaseManager.java create mode 100644 backup/src/main/java/Sdf1_login/EmailManager.java create mode 100644 backup/src/main/java/Sdf1_login/GUIManager.java create mode 100644 backup/src/main/java/Sdf1_login/GarbageManager.java create mode 100644 backup/src/main/java/Sdf1_login/GiftManager.java create mode 100644 backup/src/main/java/Sdf1_login/IPGroupManager.java create mode 100644 backup/src/main/java/Sdf1_login/InventorySerializer.java create mode 100644 backup/src/main/java/Sdf1_login/InviteManager.java create mode 100644 backup/src/main/java/Sdf1_login/IronBlockElevator.java create mode 100644 backup/src/main/java/Sdf1_login/LoginManager.java create mode 100644 backup/src/main/java/Sdf1_login/Main.java create mode 100644 backup/src/main/java/Sdf1_login/MenuIconManager.java create mode 100644 backup/src/main/java/Sdf1_login/MenuManager.java create mode 100644 backup/src/main/java/Sdf1_login/OrderManager.java create mode 100644 backup/src/main/java/Sdf1_login/PVPManager.java create mode 100644 backup/src/main/java/Sdf1_login/PasswordUtils.java create mode 100644 backup/src/main/java/Sdf1_login/PlayerStorage.java create mode 100644 backup/src/main/java/Sdf1_login/PointsManager.java create mode 100644 backup/src/main/java/Sdf1_login/QuestStorage.java create mode 100644 backup/src/main/java/Sdf1_login/QuestTracker.java create mode 100644 backup/src/main/java/Sdf1_login/RadioDownloadListener.java create mode 100644 backup/src/main/java/Sdf1_login/RadioManager.java create mode 100644 backup/src/main/java/Sdf1_login/RefundRecord.java create mode 100644 backup/src/main/java/Sdf1_login/RiskControlManager.java create mode 100644 backup/src/main/java/Sdf1_login/SalesStatsManager.java create mode 100644 backup/src/main/java/Sdf1_login/ShopCategory.java create mode 100644 backup/src/main/java/Sdf1_login/ShopItem.java create mode 100644 backup/src/main/java/Sdf1_login/ShopManager.java create mode 100644 backup/src/main/java/Sdf1_login/TicketManager.java create mode 100644 backup/src/main/java/Sdf1_login/UpdateChecker.java create mode 100644 backup/src/main/java/Sdf1_login/UserGroupManager.java create mode 100644 backup/src/main/java/Sdf1_login/VerificationManager.java create mode 100644 backup/src/main/java/Sdf1_login/WelcomeManager.java create mode 100644 backup/src/main/resources/plugin.yml create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/AFKManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/AccountRequestManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/AreaProtection.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/BondManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/BondPrinter.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/CDKManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatFilterManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatInputManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/CheckInManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/CommissionManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ConfigManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/CypayCommand.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/DatabaseManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/EmailManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/GUIManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/GarbageManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/GiftManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/IPGroupManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/InventorySerializer.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/InviteManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/IronBlockElevator.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/LoginManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/Main.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/MenuIconManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/MenuManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/OrderManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/PVPManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/PasswordUtils.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/PlayerStorage.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/PointsManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestStorage.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestTracker.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioDownloadListener.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/RefundRecord.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/RiskControlManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/SalesStatsManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopCategory.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopItem.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/TicketManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/UpdateChecker.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/UserGroupManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/VerificationManager.java create mode 100644 backup/src_backup_20260605_125051/main/java/Sdf1_login/WelcomeManager.java create mode 100644 backup/src_backup_20260605_125051/main/resources/plugin.yml create mode 100644 src/main/java/Sdf1_login/IronBlockElevator.java create mode 100644 src/main/java/Sdf1_login/TreasureBridge.java diff --git a/backup/20260605_152529/.gitattributes b/backup/20260605_152529/.gitattributes new file mode 100644 index 0000000..d49c874 --- /dev/null +++ b/backup/20260605_152529/.gitattributes @@ -0,0 +1,5 @@ +*.md text=auto encoding=UTF-8 +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary \ No newline at end of file diff --git a/backup/20260605_152529/.gitignore b/backup/20260605_152529/.gitignore new file mode 100644 index 0000000..fe405f6 --- /dev/null +++ b/backup/20260605_152529/.gitignore @@ -0,0 +1,46 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +.kotlin + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + +# 开发辅助文件(本地保留,不推送到仓库) +txt/ +*.xlsx +*.patch +新建*.txt +群戏归类.txt \ No newline at end of file diff --git a/backup/20260605_152529/AreaProtection.java.backup b/backup/20260605_152529/AreaProtection.java.backup new file mode 100644 index 0000000..bc53d53 --- /dev/null +++ b/backup/20260605_152529/AreaProtection.java.backup @@ -0,0 +1,4907 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import java.sql.*; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.GameMode; +import org.bukkit.event.EventPriority; + + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.entity.Item; + + + + +public class AreaProtection implements Listener { + + private final Main plugin; + public List giveEffects = new ArrayList<>(); + private final Map> playerAreas + = new HashMap<>(); + private final Map areas = + new ConcurrentHashMap<>(); + private Connection dbConnection; + + // 全局白名单(玩家) + private final Set globalPlayerWhitelist = + ConcurrentHashMap.newKeySet(); + // 区域白名单 key=区域名 value=玩家名集合 + private final Map> areaPlayerWhitelist = + new ConcurrentHashMap<>(); + // 全局黑名单(物品ID) + private final Set globalItemBlacklist = + ConcurrentHashMap.newKeySet(); + // 区域黑名单 + private final Map> areaItemBlacklist = + new ConcurrentHashMap<>(); + + // 选地 + private static final Material WAND = Material.BREEZE_ROD; + + private final Map pos1 = + new ConcurrentHashMap<>(); + private final Map pos2 = + new ConcurrentHashMap<>(); + private final Set selecting = + ConcurrentHashMap.newKeySet(); + public boolean denyRaid = false; + // ===== 统一文件夹 ===== + private final File rootDir; + private final File whitelistDir; + private final File globalWhitelistFile; + private final File oldDbFile; + private final File newDbFile; + + // 边框显示 + private final Map displayTaskIds = + new ConcurrentHashMap<>(); + private final Map> globalWhitelist + = new HashMap<>(); + private final Map> areaWhitelist + = new HashMap<>(); + private final Map protectedEntities + = new ConcurrentHashMap<>(); + // 区域给予的效果记录(贴标) + private final Map> playerAppliedEffects = new HashMap<>(); + // 延时清理任务 + private static final long MOVE_DEBOUNCE_MS = 100; + // 新:全部改为线程安全 + private final Map playerCurrentArea + = new ConcurrentHashMap<>(); + private final Map> playerMarkedEffects + = new ConcurrentHashMap<>(); + private final Map pendingClearTask + = new ConcurrentHashMap<>(); + private final Map pendingClearArea + = new ConcurrentHashMap<>(); + private final Map lastMoveProcess + = new ConcurrentHashMap<>(); + // 防止传送时重复处理 + private final Set teleporting + = ConcurrentHashMap.newKeySet(); + // 待清理的效果列表(与延时任务配合) + private final Map> pendingClearEffects + = new ConcurrentHashMap<>(); + + + +// ==================== 强制游戏模式 ==================== + + /** + * 监听玩家尝试切换游戏模式 + * 如果在强制游戏模式区域且不在豁免名单中,则取消切换 + */ + // 防止递归的标记 + private final Set modeSwitching + = ConcurrentHashMap.newKeySet(); + @EventHandler(priority = EventPriority.HIGHEST) + public void onGameModeChange( + PlayerGameModeChangeEvent event) { + if (event.isCancelled()) return; + + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + if (modeSwitching.contains(uid)) return; + + String areaName = playerCurrentArea.get(uid); + if (areaName == null) return; + + AreaConfig ac = areas.get(areaName); + if (ac == null) return; + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) return; + if (isExemptFromGameMode(p, areaName)) return; + + event.setCancelled(true); + + modeSwitching.add(uid); + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } finally { + modeSwitching.remove(uid); + } + + // ★ 切换后发一次中文提示 + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + ac.enforceGameMode + + " 模式,无法切换")); + } + + public Set getAlreadyForced() { + return alreadyForced; + } + + + // ==================== 定时强制游戏模式 ==================== + + private BukkitTask enforceTask; + + /** + * 启动定时强制任务 + * 每秒检查一次所有在线玩家 + */ + private BukkitTask enforceTask1; + + // 记录上次强制模式的玩家(避免重复发消息) + private final Set alreadyForced + = ConcurrentHashMap.newKeySet(); + + public void startEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + } + enforceTask = new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String areaName = + playerCurrentArea.get(uid); + if (areaName == null) { + // 不在任何区域,清除标记 + alreadyForced.remove(uid); + continue; + } + + AreaConfig ac = areas.get(areaName); + if (ac == null) continue; + if (ac.enforceGameMode == null + || ac.enforceGameMode + .isEmpty()) continue; + if (isExemptFromGameMode( + p, areaName)) { + alreadyForced.remove(uid); + continue; + } + + GameMode target = + GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + modeSwitching.add(uid); + try { + p.setGameMode(target); + } finally { + modeSwitching.remove(uid); + } + + // ★ 只在玩家已被强制过时才发消息 + // 首次不发,后续才提醒 + if (alreadyForced.contains(uid)) { + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + + ac.enforceGameMode + + " 模式,无法切换")); + } + alreadyForced.add(uid); + } else { + // 模式一致,清除标记 + alreadyForced.remove(uid); + } + } + } + }.runTaskTimer(plugin, 20L, 20L); + } + + public void stopEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + enforceTask = null; + } + } + + + + /** + * 检查玩家是否豁免于游戏模式强制 + * 豁免条件:全局白名单 / 区域白名单 / 模式排除名单 + */ + private boolean isExemptFromGameMode( + Player p, String areaName) { + String name = p.getName(); + + /* if (p.isOp()) { + plugin.getLogger().info( + "[防护-调试] 豁免原因: OP, 玩家=" + name); + return true; + }*/ + if (globalPlayerWhitelist.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 全局白名单, 玩家=" + + name);*/ + return true; + } + Set aw = + areaPlayerWhitelist.get(areaName); + if (aw != null && aw.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 区域白名单, 玩家=" + + name + " 区域=" + areaName);*/ + return true; + } + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 模式排除名单, 玩家=" + + name);*/ + return true; + } + /* plugin.getLogger().info( + "[防护-调试] 无豁免, 玩家=" + name);*/ + return false; + } + + /** + * 玩家进入区域时,如果该区域强制游戏模式, + * 则立即设置(排除豁免玩家) + */ + private void applyEnforcedGameMode(Player p, + String areaName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: ac=null, area=" + + areaName);*/ + return; + } + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: 未配置强制模式, area=" + + areaName);*/ + return; + } + + boolean exempt = isExemptFromGameMode( + p, areaName); + /* plugin.getLogger().info( + "[防护-调试] applyMode: 玩家=" + p.getName() + + " 区域=" + areaName + + " 强制=" + ac.enforceGameMode + + " 当前=" + p.getGameMode() + + " 豁免=" + exempt + + " OP=" + p.isOp() + + " 全白=" + globalPlayerWhitelist + .contains(p.getName()));*/ + + if (exempt) return; + + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + /* plugin.getLogger().info( + "[防护] 强制切换 " + p.getName() + + " → " + ac.enforceGameMode);*/ + } + } catch (Exception e) { + /* plugin.getLogger().warning( + "[防护] 强制模式失败: " + + e.getMessage());*/ + } + } + + public AreaProtection(Main plugin) { + this.plugin = plugin; + + // 统一根目录 + rootDir = new File(plugin.getDataFolder(), "区域防护"); + if (!rootDir.exists()) rootDir.mkdirs(); + + // 白名单子目录 + whitelistDir = new File(rootDir, "whitelists"); + if (!whitelistDir.exists()) whitelistDir.mkdirs(); + globalWhitelistFile = new File( + whitelistDir, "全局白名单.txt"); + + // 数据库迁移 + oldDbFile = new File( + plugin.getDataFolder(), "area_effects.db"); + newDbFile = new File(rootDir, "area_effects.db"); + migrateDatabase(); + + // 初始化 + initDatabase(); + writeDefaultConfig(); + loadAllAreas(); + loadWhitelists(); + recoverPendingEffects(); + } + public void recoverPendingEffects() { + try { + // 1. 遍历所有在线玩家(而不是遍历DB记录) + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 2. 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + // 3. 获取该玩家在DB中的所有历史记录 + List recordedAreas = + getPlayerAreas(uid); + + // 4. 清理不在当前区域的旧记录 + for (String recordedArea : recordedAreas) { + if (currentAc == null + || !currentAc.name + .equals(recordedArea)) { + // 玩家不在这个区域了 → 清DB + removePlayerEffects( + uid, recordedArea); + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在" + recordedArea + + ", 清理旧记录"); + } + } + + // 5. 如果在区域中 → 恢复/给予效果 + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } else { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在任何区域, 跳过"); + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 恢复效果失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + // 取消延时清理任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 清标记 + playerMarkedEffects.remove(uid); + playerCurrentArea.remove(uid); + } + + +// ==================== 数据库迁移 ==================== + + /** + * 如果旧位置有 db 文件,移到新位置 + * 如果新位置已有,不动 + * 如果旧位置也没有,在新位置创建 + */ + + private void migrateDatabase() { + if (newDbFile.exists()) { + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + oldDbFile.delete(); + } + return; + } + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + try { + java.nio.file.Files.move( + oldDbFile.toPath(), + newDbFile.toPath(), + java.nio.file.StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 迁移数据库失败: " + + e.getMessage()); + } + } + } + + + // 保存玩家进入区域时给予的效果 + private void savePlayerEffects(UUID uid, String playerName, + String areaName, List effects) { + try { + // 先删除该区域旧记录,避免重复 + PreparedStatement del = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + del.setString(1, uid.toString()); + del.setString(2, areaName); + del.executeUpdate(); + del.close(); + + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)"); + long now = System.currentTimeMillis(); + for (PotionEffectType type : effects) { + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, type.getName()); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, now); + ps.addBatch(); + } + ps.executeBatch(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 保存效果失败: " + e.getMessage()); + } + } + + // 查询玩家在某个区域获得的效果 + private List getPlayerEffects( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + PotionEffectType type = PotionEffectType + .getByName(rs.getString("effect_type")); + if (type != null) list.add(type); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + // 删除玩家在某个区域的效果记录 + private void removePlayerEffects(UUID uid, String areaName) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除效果失败: " + e.getMessage()); + } + } + + // 删除玩家所有效果记录 + private void removeAllPlayerEffects(UUID uid) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除全部效果失败: " + e.getMessage()); + } + } + + // 获取玩家所有记录的区域 + private List getPlayerAreas(UUID uid) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT DISTINCT area_name " + + "FROM player_effects WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("area_name")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询区域失败: " + e.getMessage()); + } + return list; + } + + // 查询所有待处理的效果(跨服重启也能恢复) + private List getAllPendingEffects() { + List list = new ArrayList<>(); + try { + Statement stmt = dbConnection.createStatement(); + ResultSet rs = stmt.executeQuery( + "SELECT uuid, player_name, area_name, " + + "effect_type FROM player_effects"); + while (rs.next()) { + list.add(new String[]{ + rs.getString("uuid"), + rs.getString("player_name"), + rs.getString("area_name"), + rs.getString("effect_type")}); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询全部效果失败: " + e.getMessage()); + } + return list; + } + + + private void writeDefaultConfig() { + File f = new File(rootDir, "末地保护区.txt"); + if (f.exists()) return; + + String endWorld = "world_the_end"; + for (org.bukkit.World w : Bukkit.getWorlds()) { + if (w.getEnvironment() + == org.bukkit.World.Environment.THE_END) { + endWorld = w.getName(); + break; + } + } + + final String ew = endWorld; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 末地保护区"); + pw.println("起点: " + ew + ",-30000000,-30000000"); + pw.println("终点: " + ew + ",30000000,30000000"); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("没收物品: BROWN_MUSHROOM,RED_MUSHROOM,MUSHROOM_STEW"); + pw.println("没收提示: 你携带了违禁品「蘑菇」,已被全部没收!"); + pw.println("通报批评: {player} 在末地违规携带蘑菇!"); + pw.println(); + pw.println("禁止放置方块"); + pw.println("禁止破坏方块"); + pw.println("禁止PVP"); + pw.println("禁止袭击"); + pw.println(); + pw.println("效果: 夜视 1 999"); + pw.println("进入提示: 末地保护区 - 请遵守规则"); + pw.println("离开提示: 已离开末地保护区"); + pw.close(); + plugin.getLogger().info("[防护] 生成末地配置, 世界: " + ew); + } catch (IOException ignored) { + } + } + + public void reload() { + loadAllAreas(); + loadWhitelists(); + // 强制输出醒目的中文日志 + plugin.getLogger().info("========================================"); + plugin.getLogger().info("[防护] 已重载 " + areas.size() + " 个区域"); + plugin.getLogger().info("[防护] 已重载白名单"); + plugin.getLogger().info("========================================"); + } + +// ==================== 消息格式化 ==================== + + /** + * 格式化区域消息 + * 1. & → §(颜色符号转换) + * 2.
和 \n → 换行 + * 3. 内容 → §l内容§r(加粗) + * 4. 自动添加 [区域防护] 前缀 + */ + private String formatAreaMsg(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + String msg = raw; + + // & → § + for (int i = 0; i <= 9; i++) { + msg = msg.replace("&" + i, "§" + i); + } + for (char c : "abcdefklnor".toCharArray()) { + msg = msg.replace("&" + c, "§" + c); + } + + // 换行 + msg = msg.replace("
", "\n"); + msg = msg.replace("\\n", "\n"); + + // HTML → Minecraft格式(栈式解析,支持嵌套) + msg = convertHtmlTags(msg); + + // 前缀 + msg = "§c§l【区域防护】§r " + msg; + + return msg; + } + + /** + * 栈式HTML标签解析 + * 处理 text rest 这样的嵌套标签 + * 内层关闭时重新应用外层格式 + */ + private String convertHtmlTags(String msg) { + StringBuilder sb = new StringBuilder(); + java.util.Deque stack = + new ArrayDeque<>(); + int i = 0; + + while (i < msg.length()) { + if (msg.charAt(i) == '<' + && i + 1 < msg.length()) { + if (msg.charAt(i + 1) == '/') { + // 关闭标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 2, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0 && !stack.isEmpty() + && stack.peek() == code) { + stack.pop(); + if (!stack.isEmpty()) { + // 重置后重新应用外层格式 + sb.append("§r"); + for (char c : stack) { + sb.append("§").append(c); + } + } else { + sb.append("§r"); + } + } + i = end + 1; + } else { + // 开放标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 1, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0) { + stack.push(code); + sb.append("§").append(code); + } + i = end + 1; + } + } else { + sb.append(msg.charAt(i)); + i++; + } + } + return sb.toString(); + } + + private char htmlToMc(String tag) { + switch (tag) { + case "b": return 'l'; + case "i": return 'o'; + case "u": return 'n'; + case "s": return 'm'; + default: return 0; + } + } + +// =============== 加载 ==================== + + public void loadAllAreas() { + areas.clear(); + File[] files = rootDir.listFiles( + (File d, String n) -> n.endsWith(".txt")); + if (files == null) return; + for (File f : files) { + try { + String name = f.getName() + .replace(".txt", ""); + AreaConfig ac = parseArea(name, f); + areas.put(name, ac); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载失败: " + f.getName()); + } + } + } + + + private void initDatabase() { + try { + dbConnection = DriverManager.getConnection( + "jdbc:sqlite:" + newDbFile.getAbsolutePath()); + Statement stmt = dbConnection.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS player_effects (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "uuid TEXT NOT NULL," + + "player_name TEXT," + + "area_name TEXT NOT NULL," + + "effect_type TEXT NOT NULL," + + "effect_level INTEGER DEFAULT 0," + + "effect_duration INTEGER DEFAULT 999," + + "enter_time INTEGER NOT NULL)"); + stmt.executeUpdate( + "CREATE INDEX IF NOT EXISTS idx_uuid " + + "ON player_effects(uuid)"); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().severe( + "[防护] 数据库初始化失败: " + + e.getMessage()); + } + } + + +// ==================== 效果名称中英文映射 ==================== + + /** + * 将中文效果名转为 Bukkit 能识别的英文名 + */ + private String resolveEffectName(String name) { + if (name == null) return null; + String n = name.trim(); + // 如果已经是英文或命名空间格式,直接用 + if (n.contains(":") + || n.equals(n.toUpperCase())) { + return n; + } + // 中文 → 英文映射 + switch (n) { + case "伤害吸收": return "ABSORPTION"; + case "生命提升": return "HEALTH_BOOST"; + case "抗性提升": return "DAMAGE_RESISTANCE"; + case "抗火": return "FIRE_RESISTANCE"; + case "夜视": return "NIGHT_VISION"; + case "力量": return "INCREASE_DAMAGE"; + case "速度": return "SPEED"; + case "急迫": return "FAST_DIGGING"; + case "跳跃提升": return "JUMP"; + case "再生": return "REGENERATION"; + case "生命恢复": return "REGENERATION"; + case "治疗": return "HEAL"; + case "瞬间治疗": return "HEAL"; + case "伤害药水": return "HARM"; + case "瞬间伤害": return "HARM"; + case "虚弱": return "WEAKNESS"; + case "中毒": return "POISON"; + case "缓慢": return "SLOW"; + case "挖掘疲劳": return "SLOW_DIGGING"; + case "失明": return "BLINDNESS"; + case "隐身": return "INVISIBILITY"; + case "发光": return "GLOWING"; + // 村庄英雄 + case "村庄英雄": return "HERO_OF_THE_VILLAGE"; + case "幸运": return "LUCK"; + case "不幸": return "UNLUCK"; + case "水下呼吸": return "WATER_BREATHING"; + case "水下速掘": return "DOLPHINS_GRACE"; + case "缓降": return "LEVITATION"; + case "漂浮": return "LEVITATION"; + case "饱和": return "SATURATION"; + // 无效效果 + case "伤害": return "HARM"; + case "治疗药水": return "HEAL"; + default: + // 尝试直接用英文匹配 + return n; + } + } + + /** + * 全量容错:把全角字符统一转半角, + * 去掉零宽字符/不可见字符, + * 让用户怎么写都能正确解析。 + */ + private String normalizeLine(String input) { + if (input == null || input.isEmpty()) return ""; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + + // 跳过零宽字符和不可见字符 + if (c == '\u200B' // 零宽空格 + || c == '\u200C' // 零宽非连接 + || c == '\u200D' // 零宽连接 + || c == '\uFEFF' // BOM + || c == '\u00A0' // 不间断空格 + || c == '\u2028' // 行分隔符 + || c == '\u2029' // 段分隔符 + || c == '\u200E' // LTR标记 + || c == '\u200F' // RTL标记 + || c == '\u2060' // 字连接符 + || (c >= '\u2000' && c <= '\u200A')) { // 各种空格 + continue; + } + + // 全角→半角映射 + if (c >= '\uFF01' && c <= '\uFF5E') { + c = (char) (c - 0xFEE0); + } + + // 全角空格→半角空格 + if (c == '\u3000') { + c = ' '; + } + + sb.append(c); + } + + return sb.toString(); + } + + + private AreaConfig parseArea(String name, File f) + throws IOException { + AreaConfig ac = new AreaConfig(); + ac.name = name; + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean inBlockComment = false; + boolean inHtmlComment = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; +// ===== 全量容错预处理:全角→半角,去零宽字符 ===== + line = normalizeLine(line); + if (line.isEmpty()) continue; +// ===== 暴力调试:输出所有包含关键词的行 ===== + if (line.indexOf("游戏") >= 0 + || line.indexOf("模式") >= 0 + || line.indexOf("生存") >= 0 + || line.indexOf("创造") >= 0 + || line.indexOf("排除") >= 0 + || line.indexOf("豁免") >= 0 + || line.indexOf("强制") >= 0 + || line.indexOf("GameMode") >= 0 + || line.indexOf("SURVIVAL") >= 0 + || line.indexOf("CREATIVE") >= 0) { + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < line.length(); i++) { + hex.append(String.format("U+%04X ", (int) line.charAt(i))); + } + /* plugin.getLogger().info("[暴力调试] 行=[" + line + + "] 长度=" + line.length() + + " 字符码=" + hex.toString());*/ + } +// ===== 兜底扫描:确保和平白名单一定能解析 ===== + if (line.indexOf("白名单") >= 0 + || line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + int ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf('='); + if (ci < 0) ci = line.indexOf(' '); + + // 和平白名单 + if (line.indexOf("和平") >= 0 + && line.indexOf("白名单") >= 0 + && ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.peaceWhitelist.add(nm); + } + plugin.getLogger().info("[解析兜底] 和平白名单=" + + ac.peaceWhitelist); + } + + // 排除名单 + if (line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + if (ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.modeExempt.add(nm); + } + plugin.getLogger().info("[解析兜底] 排除名单=" + + ac.modeExempt); + } + } + } +// ===== 兜底结束 ===== + + if (inBlockComment) { + int endIdx = line.indexOf("*/"); + if (endIdx >= 0) { + inBlockComment = false; + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("/*")) { + int endIdx = line.indexOf("*/", 2); + if (endIdx >= 0) { + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + inBlockComment = true; + continue; + } + } + if (inHtmlComment) { + int endIdx = line.indexOf("-->"); + if (endIdx >= 0) { + inHtmlComment = false; + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("", 4); + if (endIdx >= 0) { + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + inHtmlComment = true; + continue; + } + } + if (line.startsWith("#") || line.startsWith("//")) { + continue; + } + int hashIdx = line.indexOf("#"); + if (hashIdx > 0) { + line = line.substring(0, hashIdx).trim(); + } + int slashIdx = line.indexOf("//"); + if (slashIdx > 0) { + line = line.substring(0, slashIdx).trim(); + } + if (line.isEmpty()) continue; + + if (line.startsWith("起点:")) { + String[] pp = line.substring("起点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x1 = Integer.parseInt(pp[1].trim()); + ac.z1 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("终点:")) { + String[] pp = line.substring("终点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x2 = Integer.parseInt(pp[1].trim()); + ac.z2 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("高度范围:")) { + String[] pp = line.substring("高度范围:".length()) + .trim().split("-"); + if (pp.length == 2) { + ac.yMin = Integer.parseInt(pp[0].trim()); + ac.yMax = Integer.parseInt(pp[1].trim()); + } + } else if (line.startsWith("没收物品:")) { + for (String s : line.substring("没收物品:".length()) + .trim().split("[,,]")) + ac.confiscateItems.add( + s.trim().toUpperCase()); + } else if (line.startsWith("禁止使用物品:")) { + for (String s : line.substring("禁止使用物品:".length()) + .trim().split("[,,]")) + ac.denyUseItems.add( + s.trim().toUpperCase()); + } else if (line.equals("禁止放置方块")) { + ac.denyBlockPlace = true; + } else if (line.equals("禁止破坏方块")) { + ac.denyBlockBreak = true; + } else if (line.equals("禁止PVP")) { + ac.denyPVP = true; + } else if (line.equals("禁止摔伤")) { + ac.denyFallDamage = true; + } else if (line.equals("禁止饥饿")) { + ac.denyHunger = true; + } else if (line.equals("禁止一切伤害")) { + ac.denyAllDamage = true; + } else if (line.equals("禁止丢弃物品")) { + ac.denyDrop = true; + } else if (line.equals("禁止末影珍珠")) { + ac.denyEnderPearl = true; + } else if (line.equals("禁止使用弓箭")) { + ac.denyBow = true; + } else if (line.equals("禁止骑乘")) { + ac.denyMount = true; + } else if (line.equals("禁止袭击")) { + ac.denyRaid = true; + } else if (line.equals("禁止药水")) { + ac.denyPotion = true; + } else if (line.equals("禁止爆炸")) { + ac.denyExplosion = true; + } else if (line.equals("禁止燃烧") + || line.equals("禁止火焰蔓延")) { + ac.denyFireSpread = true; + } else if (line.equals("清除所有效果") + || line.equals("清理所有效果")) { + ac.clearAllBadEffects = true; + } else if (line.startsWith("清除") + || line.startsWith("清理")) { + String effName = line.substring(2).trim(); + if (effName.endsWith("效果")) + effName = effName.substring(0, + effName.length() - 2); + if (!effName.isEmpty()) + ac.clearEffects.add(effName); + } else if (line.contains("和平模式") + && !line.contains("白名单") + && !line.contains("定时") + && !line.contains("开关")) { + ac.peaceMode = true; + plugin.getLogger().info("[解析] 和平模式已识别"); + + } else if (line.startsWith("效果")) { + String rest = line.substring(2).trim(); + // 去掉前导冒号、空格 + while (rest.length() > 0 + && (rest.charAt(0) == ':' + || rest.charAt(0) == ':' + || rest.charAt(0) == ' ' + || rest.charAt(0) == '\t')) { + rest = rest.substring(1); + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + // 第一个是效果名(可能是中文) + String effName = parts[0]; + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) {} + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) {} + } + + // ★ 用映射方法转为英文名 + String resolved = resolveEffectName(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → " + resolved + + " 等级=" + effLv + + " 时长=" + effDur); + + ac.giveEffects.add(new String[]{ + resolved, + String.valueOf(effLv), + String.valueOf(effDur)}); + // ★ 加日志确认解析结果 + PotionEffectType checkType = + resolveEffectType(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → type=" + + (checkType != null + ? checkType.getName() + : "NULL(失败)") + + " 等级=" + effLv + + " 时长=" + effDur); + + + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) { + } + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) { + } + } + ac.giveEffects.add(new String[]{ + parts[0], + String.valueOf(effLv), + String.valueOf(effDur)}); + } + } else if (line.startsWith("通报批评:")) { + ac.announceTemplate = line.substring( + "通报批评:".length()).trim(); + ac.enableAnnounce = true; + } else if (line.startsWith("没收提示:")) { + ac.confiscateMsg = line.substring( + "没收提示:".length()).trim(); + } else if (line.startsWith("进入提示:")) { + String raw = line.substring("进入提示:".length()).trim(); + ac.enterMsg = raw; + + // ★ 调试:打印原文和Unicode + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★进入提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("离开提示:")) { + String raw = line.substring("离开提示:".length()).trim(); + ac.leaveMsg = raw; + + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★离开提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("惩罚命令:")) { + ac.punishCommands.add(line.substring( + "惩罚命令:".length()).trim()); + } else if (line.startsWith("强制游戏模式:") + || line.startsWith("游戏模式:")) { + String prefix = line.startsWith("强制游戏模式:") + ? "强制游戏模式:" : "游戏模式:"; + String modeStr = line.substring(prefix.length()) + .trim().toUpperCase(); + // 容错:支持多种写法 + if (modeStr.equals("CREATIVE") || modeStr.equals("创造") + || modeStr.equals("1")) { + ac.enforceGameMode = "CREATIVE"; + } else if (modeStr.equals("SURVIVAL") || modeStr.equals("生存") + || modeStr.equals("0")) { + ac.enforceGameMode = "SURVIVAL"; + } else if (modeStr.equals("ADVENTURE") || modeStr.equals("冒险") + || modeStr.equals("2")) { + ac.enforceGameMode = "ADVENTURE"; + } else if (modeStr.equals("SPECTATOR") || modeStr.equals("旁观") + || modeStr.equals("spectator") + || modeStr.equals("3")) { + ac.enforceGameMode = "SPECTATOR"; + } + } else if (line.contains("排除名单") + || line.contains("模式排除") + || line.contains("模式豁免")) { + int colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf('='); + if (colonIdx >= 0) { + String rest = line.substring(colonIdx + 1).trim(); + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playerName = s.trim(); + if (!playerName.isEmpty()) { + ac.modeExempt.add(playerName); + } + } + } + + String rest = line.substring(/*idx*/ + 4).trim(); + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playername = s.trim(); + if (!name.isEmpty()) ac.modeExempt.add(name); + } + } + + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String wlName = s.trim(); + if (!wlName.isEmpty()) { + ac.peaceWhitelist.add(wlName); + } + } + } + + + + } else if (line.equals("禁止交互展示框") + || line.equals("禁止展示框")) { + ac.denyItemFrame = true; + + } + } + br.close(); + return ac; + } +// ===== 和平白名单操作 ===== + + // 添加生物名字到区域和平白名单 + public boolean addPeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.add(creatureName); + savePeaceWhitelist(ac); + return true; + } + + // 从区域和平白名单移除生物名字 + public boolean removePeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.remove(creatureName); + savePeaceWhitelist(ac); + return true; + } + +// ===== 模式排除名单操作 ===== + + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent e) { + Entity entity = e.getEntity(); + if (!isHostile(entity.getType().name())) return; + + Location loc = entity.getLocation(); + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + + if (ac == null || !ac.peaceMode) return; + + protectedEntities.put( + entity.getUniqueId(), + System.currentTimeMillis() + 5000); + + /* plugin.getLogger().info("[和平] " + + entity.getType().name() + + " 获得5秒保护期" + + " 区域=" + ac.name + + " 白名单=" + ac.peaceWhitelist + + " 白名单数量=" + ac.peaceWhitelist.size());*/ + } + + + public void cleanupProtectedEntities() { + long now = System.currentTimeMillis(); + int cleaned = 0; + Iterator> it = + protectedEntities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (now > entry.getValue()) { + it.remove(); + cleaned++; + } + } + if (cleaned > 0) { + /* plugin.getLogger().info("[和平模式] 清理过期保护: " + + cleaned + "个 剩余:" + + protectedEntities.size() + "个");*/ + } + } + + + public void banHostilesWithWhitelist(Player p, AreaConfig ac) { + long now = System.currentTimeMillis(); + // 看看附近到底有什么实体 + List nearby = p.getNearbyEntities(48, 48, 48); + /* plugin.getLogger().info("[和平] " + p.getName() + + " 附近实体总数=" + nearby.size());*/ + for (Entity ent : nearby) { + String typeName = ent.getType().name(); + /* plugin.getLogger().info("[和平] 实体: " + + typeName + " isHostile=" + isHostile(typeName));*/ + } + int scanned = 0; + int waiting = 0; + int saved = 0; + int banished = 0; + + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (!isHostile(ent.getType().name())) continue; + scanned++; + + UUID entUid = ent.getUniqueId(); + Long protectUntil = protectedEntities.get(entUid); + String customName = ent.getCustomName(); + + // 有保护期记录 + if (protectUntil != null) { + long remain = protectUntil - now; + + if (remain > 0) { + // 保护期内,精确剩余秒数 + int secondsLeft = (int) Math.ceil(remain / 1000.0); + waiting++; + + // 保护期内也检查名字(玩家可能刚贴上) + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + protectedEntities.remove(entUid); + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 白名单匹配,永久保留");*/ + continue; + } + + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 保护期剩余约" + secondsLeft + "秒");*/ + continue; + } + + // 保护期到了,最终检查 + protectedEntities.remove(entUid); + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + " 5秒到期");*/ + + // 到期时再查一次白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 到期但白名单匹配,保留");*/ + continue; + } + + // 没名字或不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名=[" + customName + "] 传送虚空");*/ + continue; + } + + // 没有保护期记录(非刷怪蛋生成的或记录被清了) + // 也检查一下白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 无保护期但白名单匹配,保留");*/ + continue; + } + + // 没有保护期且不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 无保护期,命名=[" + customName + "] 传送虚空");*/ + } + + /* plugin.getLogger().info("[和平] " + + " 扫描=" + scanned + + " 等待=" + waiting + + " 保留=" + saved + + " 传送=" + banished + + " 白名单=" + ac.peaceWhitelist);*/ + } + + private static final Set HOSTILE_TYPES = new HashSet<>(Arrays.asList( + "ZOMBIE", "ZOMBIE_VILLAGER", "HUSK", "DROWNED", + "SKELETON", "STRAY", "BOGGED", "WITHER_SKELETON", + "CREEPER", "SPIDER", "CAVE_SPIDER", "ENDERMITE", + "SILVERFISH", "BLAZE", "GHAST", "BIG_GHAST", + "MAGMA_CUBE", "SLIME", "WITCH", + "GUARDIAN", "ELDER_GUARDIAN", + "PILLAGER", "VINDICATOR", "EVOKER", "RAVAGER", + "ILLUSIONER", "VEX", + "PHANTOM", "SHULKER", "WARDEN", "BREEZE", + "PIGLIN", "PIGLIN_BRUTE", "ZOGLIN", + "ZOMBIFIED_PIGLIN", "ENDER_DRAGON" + )); + + private boolean isHostile(String typeName) { + return HOSTILE_TYPES.contains(typeName); + } + + // ==================== 白名单持久化 ==================== + + /** + * 保存白名单到文件 + * 路径:区域防护/whitelists/全局白名单.txt + * 路径:区域防护/whitelists/区域名.txt + */ + public void saveWhitelists() { + try { + // 1. 保存全局白名单 + PrintWriter pwGlobal = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(globalWhitelistFile), + StandardCharsets.UTF_8)); + for (String name : globalPlayerWhitelist) { + pwGlobal.println(name); + } + pwGlobal.close(); + + // 2. 删除旧的区域白名单文件(防止残留) + File[] oldFiles = whitelistDir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (oldFiles != null) { + for (File f : oldFiles) f.delete(); + } + + // 3. 保存各区域白名单 + for (Map.Entry> entry + : areaPlayerWhitelist.entrySet()) { + String name = entry.getKey(); + File areaFile = new File( + whitelistDir, name + ".txt"); + PrintWriter pwArea = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(areaFile), + StandardCharsets.UTF_8)); + for (String playerName : entry.getValue()) { + pwArea.println(playerName); + } + pwArea.close(); + } + + plugin.getLogger().info("[防护] 白名单已保存"); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 保存白名单失败: " + + e.getMessage()); + } + } + + + + // ==================== 区域检测 ==================== + + public AreaConfig getArea(String worldName, + int x, int y, int z) { + for (AreaConfig ac : areas.values()) { + if (ac.world.equalsIgnoreCase(worldName) + && inRect(x, z, ac) + && y >= ac.yMin + && y <= ac.yMax) { + return ac; + } + } + return null; + } + + private boolean inRect(int x, int z, AreaConfig ac) { + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + return x >= minX && x <= maxX + && z >= minZ && z <= maxZ; + } + + // ==================== 白名单检查 ==================== + + private boolean isPlayerWhitelisted(String player, + AreaConfig ac) { + if (globalPlayerWhitelist.contains( + player.toLowerCase())) + return true; + Set areaList = + areaPlayerWhitelist.get(ac.name); + return areaList != null + && areaList.contains(player.toLowerCase()); + } + + private boolean isItemBlacklisted(String itemId, + AreaConfig ac) { + if (globalItemBlacklist.contains( + itemId.toUpperCase())) + return true; + Set areaList = + areaItemBlacklist.get(ac.name); + return areaList != null + && areaList.contains(itemId.toUpperCase()); + } + + // ==================== 没收 ==================== + + private int confiscateItem(Player p, String itemTypeId) { + Material mat = Material.matchMaterial(itemTypeId); + if (mat == null) return 0; + ItemStack[] contents = p.getInventory().getContents(); + int removed = 0; + for (int i = 0; i < contents.length; i++) { + if (contents[i] != null + && contents[i].getType() == mat) { + removed += contents[i].getAmount(); + contents[i] = null; + } + } + p.getInventory().setContents(contents); + return removed; + } + + // ==================== 边框显示 ==================== + private void showBorder(Player p, AreaConfig ac, boolean temp) { + cancelBorder(p); + int taskId = Bukkit.getScheduler() + .runTaskTimer(plugin, new Runnable() { + @Override + public void run() { + if (!p.isOnline()) return; + Location loc = p.getLocation(); + int px = loc.getBlockX(); + int py = loc.getBlockY(); + int pz = loc.getBlockZ(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + for (int dy = 5; dy <= 30; dy += 5) { + for (int x = minX; x <= maxX; x += 4) { + for (int z = minZ; z <= maxZ; z += 4) { + if (x == minX || x == maxX + || z == minZ || z == maxZ) { + p.spawnParticle(Particle.END_ROD, + x + 0.5, py + dy, z + 0.5, + 1, 0, 0, 0, 0); + } + } + } + } + } + }, 0L, 20L).getTaskId(); + displayTaskIds.put(p.getUniqueId(), taskId); // taskId 是 int + if (temp) { + Bukkit.getScheduler().runTaskLater(plugin, + () -> cancelBorder(p), 300L); + } + } + + + private void cancelBorder(Player p) { + Integer taskId = displayTaskIds.remove(p.getUniqueId()); + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + } + + public void onPlayerQuit(String playerName) { + playerCurrentArea.remove(playerName); + } + + public void clearAllPlayerAreas() { + playerCurrentArea.clear(); + } + private void saveModeExempt(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("排除名单") + || trimmed.contains("模式排除") + || trimmed.contains("模式豁免")) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.modeExempt.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存模式排除名单失败: " + e.getMessage()); + } + } + + // ==================== 事件监听 ==================== + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + Location to = event.getTo(); + if (to == null) return; + + Location from = event.getFrom(); + if (from.getBlockX() == to.getBlockX() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + + // 防抖 + Long lastTime = lastMoveProcess.get(uid); + long now = System.currentTimeMillis(); + if (lastTime != null + && now - lastTime < MOVE_DEBOUNCE_MS) { + return; + } + lastMoveProcess.put(uid, now); + + // 检测区域 + String oldArea = playerCurrentArea.get(uid); + String newArea = null; + String forcedMode = null; + + for (Map.Entry entry + : areas.entrySet()) { + AreaConfig ac = entry.getValue(); + if (!ac.world.equals(to.getWorld().getName())) + continue; + int px = to.getBlockX(); + int pz = to.getBlockZ(); + int py = to.getBlockY(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + if (px >= minX && px <= maxX + && pz >= minZ && pz <= maxZ + && py >= ac.yMin && py <= ac.yMax) { + newArea = entry.getKey(); + forcedMode = ac.enforceGameMode; + break; + } + } + + /* plugin.getLogger().info( + "[防护-移动] " + p.getName() + + " 旧=[" + oldArea + + "] 新=[" + newArea + "]" + + " areas.size=" + areas.size() + + " world=" + to.getWorld().getName());*/ + + // ===== 2. 区域没变 ===== + if (Objects.equals(oldArea, newArea)) { + if (newArea != null) { + AreaConfig ac = areas.get(newArea); + if (ac != null) { + // ★ 每次移动都清除指定效果 + clearBadEffects(p, ac); + + // ★ 每次移动都强制游戏模式 + if (ac.enforceGameMode != null + && !ac.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + } + return; + } + + // 离开旧区域 + if (oldArea != null) { + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★开始离开处理: + + oldArea);*/ + + AreaConfig oldAc = areas.get(oldArea); + + // 离开提示 + if (oldAc != null && oldAc.leaveMsg != null + && !oldAc.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(oldAc.leaveMsg)); + } + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★收标: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + plugin.getLogger().info( + "[防护-移动-离开] 标记项: " + t.getName()); + } + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + plugin.getLogger().info( + "[防护-移动-离开] 取消旧延时"); + } + + // 创建延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = oldArea; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★创建延时任务: " + + toClear.size() + "个");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-移动-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); + + plugin.getLogger().info( + "[防护-移动-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size()); + + if (online == null || !online.isOnline()) { + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-移动-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-移动-清理] + cleared" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + } + } + + + // 进入新区域 + if (newArea != null) { + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + if (oldP != null) { + diffEffects(p, uid, oldP, newArea); + } + } + + AreaConfig newAc = areas.get(newArea); + if (newAc != null) { + if (newAc.enterMsg != null + && !newAc.enterMsg.isEmpty()) { + p.sendMessage( + formatAreaMsg(newAc.enterMsg)); + } + applyRegionEffects(p, uid, newArea, newAc); + // 清除效果 + clearBadEffects(p, newAc); + + } +// ===== 清除指定效果 ===== + if (newAc.clearEffects != null + && !newAc.clearEffects.isEmpty()) { + for (String effName : newAc.clearEffects) { + PotionEffectType type = + resolveEffectType(effName); + if (type != null && p.getPotionEffect(type) != null) { + p.removePotionEffect(type); + plugin.getLogger().info( + "[防护] ★清除效果: " + effName + + " 玩家=" + p.getName()); + } + } + } + +// ===== 清除所有效果 ===== + if (newAc.clearAllBadEffects) { + for (PotionEffect eff : p.getActivePotionEffects()) { + p.removePotionEffect(eff.getType()); + } + plugin.getLogger().info( + "[防护] ★清除所有效果, 玩家=" + p.getName()); + } + + if (forcedMode != null + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(forcedMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + if (newArea != null) { + playerCurrentArea.put(uid, newArea); + } else { + playerCurrentArea.remove(uid); + } + } +// ===== 清理袭击生物 ===== + + // 需要清理的袭击生物类型 + private static final Set RAID_MOBS = new HashSet<>(Arrays.asList( + "VEX", + "EVOKER", + "PILLAGER", + "VINDICATOR", + "WITCH", + "RAVAGER", + "PILLAGER" + )); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + Entity entity = event.getEntity(); + Location loc = entity.getLocation(); + + // 检查是否在防护区域内 + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + if (ac == null) return; + + // 禁止袭击:取消袭击事件 + if (ac.denyRaid) { + // 清理袭击生物 + String typeName = entity.getType().name(); + if (RAID_MOBS.contains(typeName)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] ★清理袭击生物: " + typeName + + " 区域=" + ac.name); + } + } + + // 没收物品检测 + if (ac.confiscateItems != null + && !ac.confiscateItems.isEmpty()) { + if (entity instanceof Item) { + Item item = (Item) entity; + String itemType = item.getItemStack() + .getType().name(); + if (ac.confiscateItems.contains(itemType)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] 没收违禁掉落物: " + + itemType); + } + } + } + } + + + /** + * 进入区域时发放效果 + * 流程:查DB → 有记录跳过写入 → 无记录写入DB + * → 给效果 → 贴标 + */ + private void applyRegionEffects(Player p, UUID uid, + String areaName, AreaConfig ac) { + /* plugin.getLogger().info( + "[防护-贴标] ★★★方法被调用! 玩家=" + + p.getName() + " 区域=" + areaName + + " ac=" + (ac != null) + + " 效果配置数=" + + (ac != null ? ac.giveEffects.size() : "null"));*/ + + if (ac == null || ac.giveEffects.isEmpty()) return; + if (ac.giveEffects.isEmpty()) return; + // 查DB:该玩家在此区域是否已有效果记录 + List dbEffects = + getPlayerEffectNames(uid, areaName); + + if (dbEffects.isEmpty()) { + // 无记录 → 写入DB + for (String[] eff : ac.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t == null) continue; + saveSingleEffect(uid, p.getName(), + areaName, t.getName()); + } + plugin.getLogger().info( + "[防护] DB写入, 玩家=" + p.getName() + + " 区域=" + areaName); + } else { + plugin.getLogger().info( + "[防护] DB已有记录, 跳过写入, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + + // 给效果 + 贴标 + List applied = new ArrayList<>(); + for (String[] eff : ac.giveEffects) { + try { + PotionEffectType type = + resolveEffectType(eff[0]); + if (type == null) continue; + int lv = Integer.parseInt(eff[1]) - 1; + int dur = Integer.parseInt(eff[2]) * 20; + + PotionEffect existing = + p.getPotionEffect(type); + if (existing != null + && existing.getAmplifier() >= lv) { + continue; + } + p.addPotionEffect( + new PotionEffect(type, dur, lv)); + applied.add(type); + } catch (Exception ignored) { + } + } + + // 贴标 + // 新代码(合并,不覆盖): + List existingMarks = + playerMarkedEffects.get(uid); + if (existingMarks != null && !existingMarks.isEmpty()) { + // 已有标记,合并新增的 + for (PotionEffectType t : applied) { + if (!existingMarks.contains(t)) { + existingMarks.add(t); + } + } + /* plugin.getLogger().info( + "[防护-贴标] 合并标记: " + + existingMarks.size() + + " 个, 玩家=" + p.getName());*/ + } else { + // 无标记,新建 + playerMarkedEffects.put(uid, applied); + /* plugin.getLogger().info( + "[防护-贴标] 新建标记: " + + applied.size() + + " 个, 玩家=" + p.getName());*/ + } + } + + /** + * 离开区域时,延时15秒清理效果 + * 流程:收标 → 15秒后清效果 → 确认清空 → 清DB + */ + private void scheduleEffectClear(Player p, UUID uid, + String areaName) { + // 收标 + List marked = + playerAppliedEffects.remove(uid); + + plugin.getLogger().info( + "[防护] 收标 " + (marked != null ? marked.size() : 0) + + " 个, 玩家=" + p.getName() + + " 区域=" + areaName); + + // 启动15秒延时 + pendingClearArea.put(uid, areaName); + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + + // 清除效果 + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + + // 确认清空后清DB + removePlayerEffects(uid, areaName); + plugin.getLogger().info( + "[防护] 15秒已到, 清除 " + + (marked != null ? marked.size() : 0) + + " 个效果 + DB, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + }.runTaskLater(plugin, 300L); // 15秒 = 300 tick + + pendingClearTask.put(uid, task); + } + public Set getAllAreaNames() { + return areas.keySet(); + } + + /** + * 15秒内进了新区域: + * 清除旧区域有、新区域没有的效果 + */ + private void diffEffects(Player p, UUID uid, + String oldAreaName, String newAreaName) { + AreaConfig oldAc = areas.get(oldAreaName); + AreaConfig newAc = areas.get(newAreaName); + if (oldAc == null || newAc == null) return; + + // ★ 从 pendingClearEffects 获取旧标记 + List oldMarked = + pendingClearEffects.remove(uid); + + if (oldMarked == null || oldMarked.isEmpty()) { + plugin.getLogger().info( + "[防护-跨区域] 无待清理效果"); + return; + } + + // 新区域的效果集合 + Set newTypes = new HashSet<>(); + if (newAc != null) { + for (String[] eff : newAc.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t != null) newTypes.add(t); + } + } + + int cleared = 0; + for (PotionEffectType type : oldMarked) { + if (!newTypes.contains(type)) { + p.removePotionEffect(type); + cleared++; + plugin.getLogger().info( + "[防护-跨区域] 清除: " + + type.getName()); + } else { + plugin.getLogger().info( + "[防护-跨区域] 保留: " + + type.getName() + + " (新区域也有)"); + } + } + + removePlayerEffects(uid, oldAreaName); + plugin.getLogger().info( + "[防护-跨区域] 完成: 清除" + + cleared + "/" + oldMarked.size() + + " 个, " + oldAreaName + + " → " + newAreaName); + } + +// ===== DB 辅助方法(简化版)===== + + /** + * 查询玩家在某个区域的效果记录(返回英文名列表) + */ + private List getPlayerEffectNames( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid=? AND area_name=?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("effect_type")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + /** + * 写入单条效果记录 + */ + private void saveSingleEffect(UUID uid, + String playerName, String areaName, + String effectType) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?,?,?,?,?,?,?)"); + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, effectType); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 写入效果失败: " + e.getMessage()); + } + } + + // ==================== 违禁品没收 ==================== + + /** + * 检查玩家是否豁免于没收 + * 全局白名单 / 区域白名单 / modeExempt / OP 都跳过 + */ + private boolean isExemptFromConfiscation( + Player p, String areaName) { + String name = p.getName(); + + if (globalPlayerWhitelist.contains(name)) + return true; + + Set areaWl = + areaPlayerWhitelist.get(areaName); + if (areaWl != null && areaWl.contains(name)) + return true; + + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) + return true; + + return false; + } + + /** + * 没收玩家背包中的违禁品 + */ + private void confiscateItems(Player p, + AreaConfig ac) { + boolean confiscated = false; + + for (int i = 0; + i < p.getInventory().getSize(); i++) { + ItemStack item = p.getInventory().getItem(i); + if (item == null) continue; + + String typeName = item.getType().name(); + if (ac.confiscateItems + .contains(typeName)) { + p.getInventory().setItem(i, null); + confiscated = true; + } + } + + if (confiscated) { + String msg = ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty() + ? ac.confiscateMsg + : "§c你的违禁品已被没收"; + p.sendMessage(msg); + + // 通报批评 + if (ac.enableAnnounce + && ac.announceTemplate != null) { + String text = ac.announceTemplate + .replace("{player}", + p.getName()); + Bukkit.broadcastMessage(text); + } + } + } + + + + private String getChineseGameMode(GameMode mode) { + switch (mode) { + case SURVIVAL: return "生存模式"; + case CREATIVE: return "创造模式"; + case ADVENTURE: return "冒险模式"; + case SPECTATOR: return "旁观模式"; + default: return mode.name(); + } + } + + private void handleEnter(Player p, AreaConfig ac) { + UUID uid = p.getUniqueId(); + + // 清除负面效果 + clearBadEffects(p, ac); + + // 给予效果 + List given = new ArrayList<>(); + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + given.add(type); + } + } + + removePlayerEffects(uid, ac.name); + if (!given.isEmpty()) { + // savePlayerEffects(uid, p.getName(), ac.name, given); + } + + // 白名单检查(最先执行) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 游戏模式切换(只对非白名单玩家) + if (ac.enforceGameMode != null) { + boolean exempt = isPlayerExemptFromModeChange(p, ac); + if (!exempt) { + GameMode target = GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + p.sendMessage("§e§l[区域防护] §f游戏模式已切换为: " + + getChineseGameMode(target)); + } + } + } + + // 和平模式/袭击检查 + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + handleConfiscate(p, ac); + } + + private boolean isPlayerExemptFromModeChange( + Player p, AreaConfig ac) { + // if (p.isOp()) return true; + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return true; + } + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return true; + } + if (ac.modeExempt.contains(p.getName())) { + return true; + } + return false; + } + + private void banRangedHostiles(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (isRangedHostile(ent.getType().name())) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + /** + * 按配置清除效果 + * denyRaid → 清除袭击相关 + * denyAllEffects → 清除所有负面+中性 + * denyAllEffects > denyRaid(全清优先) + */ + private void clearBadEffects(Player p, AreaConfig ac) { + // 和平模式 > 清除所有效果 > 禁止单个 + if (ac.peaceMode || ac.clearAllBadEffects) { + PotionEffectType[] allBad = { + PotionEffectType.SLOWNESS, + PotionEffectType.MINING_FATIGUE, + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.NAUSEA, + PotionEffectType.BLINDNESS, + PotionEffectType.HUNGER, + PotionEffectType.WEAKNESS, + PotionEffectType.POISON, + PotionEffectType.WITHER, + PotionEffectType.LEVITATION, + PotionEffectType.UNLUCK, + PotionEffectType.DARKNESS, + PotionEffectType.WIND_CHARGED, + PotionEffectType.WEAVING, + PotionEffectType.OOZING, + PotionEffectType.INFESTED, + PotionEffectType.BAD_OMEN, + PotionEffectType.TRIAL_OMEN, + PotionEffectType.RAID_OMEN + }; + for (PotionEffectType type : allBad) { + p.removePotionEffect(type); + } + return; + } + + // 禁止袭击 → 自动关联清除 + if (ac.denyRaid) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + p.removePotionEffect(PotionEffectType.RAID_OMEN); + } + + // 配置文件单个清除 + for (String effName : ac.clearEffects) { + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.removePotionEffect(type); + } + } + } + + + private PotionEffectType resolveEffectType(String name) { + if (name == null || name.isEmpty()) return null; + + // 去掉"效果"后缀 + String clean = name; + if (clean.endsWith("效果")) { + clean = clean.substring(0, clean.length() - 2); + } + + // 硬编码中文名→英文ID映射(不依赖任何外部Map) + switch (clean) { + // 负面 + case "缓慢": + return resolveEffectType("slowness"); + case "挖掘疲劳": + return resolveEffectType("mining_fatigue"); + case "瞬间伤害": + return resolveEffectType("instant_damage"); + case "反胃": + return resolveEffectType("nausea"); + case "失明": + return resolveEffectType("blindness"); + case "饥饿": + return resolveEffectType("hunger"); + case "虚弱": + return resolveEffectType("weakness"); + case "中毒": + return resolveEffectType("poison"); + case "凋零": + return resolveEffectType("wither"); + case "飘浮": + return resolveEffectType("levitation"); + case "霉运": + return resolveEffectType("unluck"); + case "黑暗": + return resolveEffectType("darkness"); + case "蓄风": + return resolveEffectType("wind_charged"); + case "盘丝": + return resolveEffectType("weaving"); + case "渗浆": + return resolveEffectType("oozing"); + case "寄生": + return resolveEffectType("infested"); + // 中性 + case "不祥之兆": + case "不祥征兆": + return resolveEffectType("bad_omen"); + case "袭击之兆": + case "袭击征兆": + return resolveEffectType("raid_omen"); + case "试炼之兆": + case "试炼征兆": + return resolveEffectType("trial_omen"); + // 正面 + case "迅捷": + return resolveEffectType("speed"); + case "急迫": + return resolveEffectType("haste"); + case "力量": + return resolveEffectType("strength"); + case "瞬间治疗": + return resolveEffectType("instant_health"); + case "跳跃提升": + return resolveEffectType("jump_boost"); + case "生命恢复": + return resolveEffectType("regeneration"); + case "抗性提升": + return resolveEffectType("resistance"); + case "抗火": + return resolveEffectType("fire_resistance"); + case "水下呼吸": + return resolveEffectType("water_breathing"); + case "隐身": + return resolveEffectType("invisibility"); + case "夜视": + return resolveEffectType("night_vision"); + case "发光": + return resolveEffectType("glowing"); + case "生命提升": + return resolveEffectType("health_boost"); + case "伤害吸收": + return resolveEffectType("absorption"); + case "饱和": + return resolveEffectType("saturation"); + case "幸运": + return resolveEffectType("luck"); + case "村庄英雄": + return resolveEffectType("hero_of_the_village"); + case "缓降": + return resolveEffectType("slow_falling"); + case "潮涌能量": + return resolveEffectType("conduit_power"); + case "海豚的恩惠": + return resolveEffectType("dolphins_grace"); + case "致命中毒": + return resolveEffectType("poison"); + default: + break; + } + + +// 兜底:直接用原版英文ID查(★ 注意是 PotionEffectType.getByName,不是 resolveEffectType) + PotionEffectType fallback = PotionEffectType.getByName(clean); + if (fallback != null) return fallback; + fallback = PotionEffectType.getByName(clean.toLowerCase()); + if (fallback != null) return fallback; + + plugin.getLogger().warning("[防护] 无法识别效果: " + name); + return null; + + } + + + private static final Map EFFECT_MAP + = new HashMap<>(); + + + private void clearRaidEffects(Player p) { + boolean cleared = false; + try { + if (p.hasPotionEffect(PotionEffectType.BAD_OMEN)) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + try { + if (p.hasPotionEffect(PotionEffectType.RAID_OMEN)) { + p.removePotionEffect(PotionEffectType.RAID_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + if (cleared) { + p.sendMessage("§c§l[区域防护] §f已清除袭击相关效果"); + } + } + + public Map> + getPendingClearEffects() { + return pendingClearEffects; + } + + public void onPlayerOffline(UUID uid, String playerName, + String areaName) { + // 清除该玩家在该区域的所有效果 + List given = getPlayerEffects(uid, areaName); + for (PotionEffectType type : given) { + // 只清除我们给予的效果 + Player p = Bukkit.getPlayer(uid); + if (p != null) { + p.removePotionEffect(type); + } + } + // 清除DB记录 + removePlayerEffects(uid, areaName); + plugin.getLogger().info("[防护] " + playerName + + " 下线,已清除" + given.size() + + "个效果并清理DB"); + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent e) { + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + if (from.getWorld() == to.getWorld() + && from.distance(to) < 5) return; + + Player p = e.getPlayer(); + UUID uid = p.getUniqueId(); + + // 标记传送中 + teleporting.add(uid); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + teleporting.remove(uid); + }, 5L); + + AreaConfig fromArea = getArea( + from.getWorld().getName(), + from.getBlockX(), from.getBlockY(), + from.getBlockZ()); + AreaConfig toArea = getArea( + to.getWorld().getName(), + to.getBlockX(), to.getBlockY(), + to.getBlockZ()); + + String fromName = fromArea != null + ? fromArea.name : null; + String toName = toArea != null + ? toArea.name : null; + + /* plugin.getLogger().info( + "[防护-传送] ★★★进入onTeleport: " + + p.getName() + + " [" + fromName + "] → [" + toName + "]" + + " teleporting=" + teleporting.contains(uid));*/ + + if (Objects.equals(fromName, toName)) { + /* plugin.getLogger().info( + "[防护-传送] 区域相同, 跳过");*/ + return; + } + + // ===== 离开旧区域 ===== + if (fromName != null) { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★开始离开处理: " + + fromName);*/ + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★收标结果: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + /* plugin.getLogger().info( + "[防护-传送-离开] 标记项: " + + t.getName());*/ + } + } else { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★标记为null! " + + "尝试其他key查找...");*/ + // 遍历查找可能的标记 + for (Map.Entry> entry + : playerMarkedEffects.entrySet()) { + if (entry.getKey().equals(uid)) { + /* plugin.getLogger().info( + "[防护-传送-离开] 找到标记! 效果数=" + + entry.getValue().size());*/ + } + } + /* plugin.getLogger().info( + "[防护-传送-离开] playerMarkedEffects大小=" + + playerMarkedEffects.size());*/ + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 创建15秒延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = fromName; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★创建延时任务: " + + toClear.size() + "个效果");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-传送-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); +/* + plugin.getLogger().info( + "[防护-传送-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size() + + " 区域=" + clearArea);*/ + + if (online == null + || !online.isOnline()) { + plugin.getLogger().info( + "[防护-传送-清理] 玩家不在线, 清DB"); + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-传送-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-传送-清理] ★★★完成: 清除" + + cleared + "/" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + + } + + // 离开提示 + if (fromArea != null && fromArea.leaveMsg != null + && !fromArea.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(fromArea.leaveMsg)); + } + } + + // ===== 进入新区域 ===== + if (toName != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] ★★★开始进入处理: " + + toName);*/ + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + plugin.getLogger().info( + "[防护-传送-进入] 取消旧延时, 旧区域=" + + oldP); + if (oldP != null) { + diffEffects(p, uid, oldP, toName); + } + } + + // 进入提示 + if (toArea != null && toArea.enterMsg != null + && !toArea.enterMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(toArea.enterMsg)); + } + + // 发放效果 + if (toArea != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] 调用applyRegionEffects");*/ + applyRegionEffects(p, uid, toName, toArea); +// 清除效果 + clearBadEffects(p, toArea); + + if (toArea.confiscateItems != null + && !toArea.confiscateItems.isEmpty() + && !isExemptFromConfiscation( + p, toName)) { + confiscateItems(p, toArea); + } + } + + // 强制模式 + if (toArea != null && toArea.enforceGameMode != null + && !toArea.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, toName)) { + GameMode target = GameMode.valueOf( + toArea.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + // 更新记录 + if (toName != null) { + playerCurrentArea.put(uid, toName); + } else { + playerCurrentArea.remove(uid); + } + } + + + public Map getPendingClearTasks() { + return pendingClearTask; + } + + public Map> + getPlayerMarkedEffects() { + return playerMarkedEffects; + } + + public void handleInside(Player p, AreaConfig ac) { + clearBadEffects(p, ac); + + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + // 全白或区白 → 跳过所有限制性规则(包括没收) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + handleConfiscate(p, ac); + } + + public void cleanupExpiredProtections() { + protectedEntities.entrySet().removeIf( + entry -> System.currentTimeMillis() > entry.getValue()); + } + + // 禁止袭击:只驱逐袭击怪物 + private void banRaidMobs(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(32, 32, 32)) { + String t = ent.getType().name(); + if (t.equals("PILLAGER") + || t.equals("VINDICATOR") + || t.equals("EVOKER") + || t.equals("RAVAGER") + || t.equals("VEX") + || t.equals("WITCH") + || t.equals("ILLUSIONER") + || t.equals("ZOMBIE_VILLAGER")) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(voidLoc.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + public Set getAreaNames() { + return areas.keySet(); + } + + public Collection getPendingTasks() { + return pendingClearTask.values(); + } + + + // 判断是否为远程攻击敌对生物 + private boolean isRangedHostile(String typeName) { + switch (typeName) { + // ===== 骷髅系 ===== + case "SKELETON": // 骷髅(弓箭) + case "STRAY": // 流浪者(迟缓之箭) + case "BOGGED": // 虫蚀骷髅(毒箭) + case "WITHER_SKELETON": // 凋灵骷髅(近战但算灾厄) + + // ===== 灾厄村民 ===== + case "PILLAGER": // 掠夺者(弩) + case "ILLUSIONER": // 幻术师(弓箭+失明) + case "EVOKER": // 幻魔者(唤魔) + case "VINDICATOR": // 卫道士(斧头近战) + case "VEX": // 恼鬼(剑近战,但会穿墙飞行) + + // ===== 法术/投射 ===== + case "BLAZE": // 烈焰人(火球) + case "WITCH": // 女巫(药水) + + // ===== 飞行/爆炸 ===== + case "GHAST": // 恶魂(火球) + case "PHANTOM": // 幻翼(俯冲) + case "BIG_GHAST": // 大恶魂(1.21新增) + + // ===== 水下 ===== + case "GUARDIAN": // 守卫者(激光) + case "ELDER_GUARDIAN": // 远古守卫者(激光) + + // ===== 远程特殊 ===== + case "SHULKER": // 潜影贝(潜影弹) + case "BREEZE": // 旋风人(风弹) + + // ===== 蜘蛛系(远程毒液攻击) ===== + case "SPIDER": // 蜘蛛(白天远程吐丝) + case "CAVE_SPIDER": // 洞穴蜘蛛(毒) + + // ===== 1.21新增 ===== + case "WIND_CHARGED": // 蓄风怪(风弹) + case "WEAVING": // 盘丝怪(吐丝) + case "OOZING": // 渗浆怪(史莱姆) + case "INFESTED": // 寄生怪(蠹虫) + + // ===== 盔甲/变种(部分版本) ===== + case "ARMORED_SKELETON": // 盔甲骷髅 + case "ARCHER_SKELETON": // 弓箭骷髅(部分版本) + case "SNOW_GOLEM": // 雪傀儡(不是敌对但远程) + + // ===== 亡灵骑乘 ===== + case "HUSK": // 尸壳(近战但属于灾厄系) + case "DROWNED": // 溺尸(三叉戟远程) + case "ZOMBIE_VILLAGER": // 僵尸村民 + case "ZOMBIE_PIGMAN": // 僵尸猪灵 + case "ZOMBIFIED_PIGLIN": // 僵尸猪灵(1.16+) + case "PIGLIN_BRUTE": // 猪灵蛮兵 + + // ===== 猪灵(弓箭) ===== + case "PIGLIN": // 猪灵(金弩远程) + + // ===== 末影系 ===== + case "ENDER_DRAGON": // 末影龙(远程吐息) + case "WARDEN": // 监守者(远程音波) + + // ===== 其他 ===== + case "CREPER": // 苦力怕(爆炸不算远程但危险) + case "CREEPER": // 苦力怕 + + return true; + default: + return false; + } + } + + + + private void applyEffects(Player p, AreaConfig ac) { + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + + PotionEffectType type = resolveEffectType(effName); + plugin.getLogger().info("[防护调试] 给予: " + + effName + " -> " + type); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + plugin.getLogger().info("[防护调试] 已给予: " + + effName + " 等级" + level + + " 持续" + duration + "秒"); + } + } + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockPlace + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止放置方块"); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockBreak + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏方块"); + } + } + + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + // 保护性规则:对所有人无条件生效 + if (ac.denyAllDamage) { e.setCancelled(true); return; } + if (ac.denyFallDamage + && e.getCause() == EntityDamageEvent.DamageCause.FALL) + e.setCancelled(true); + } + + + @EventHandler + public void onPVP(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyPVP + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + + @EventHandler + public void onFood(FoodLevelChangeEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyHunger) + e.setCancelled(true); + } + + + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyDrop + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止丢弃物品"); + } + } + + // 替换整个 onInteract 方法 + @EventHandler + public void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand == null) return; + + // ===== 选区工具 ===== + if (hand.getType() == Material.BREEZE_ROD) { + ItemMeta meta = hand.getItemMeta(); + if (meta != null && meta.getDisplayName() != null + && meta.getDisplayName().contains("区域选择")) { + Action action = e.getAction(); + boolean isLeft = (action == Action.LEFT_CLICK_AIR + || action == Action.LEFT_CLICK_BLOCK); + boolean isRight = (action == Action.RIGHT_CLICK_AIR + || action == Action.RIGHT_CLICK_BLOCK); + if (isLeft || isRight) { + e.setCancelled(true); + Block block = e.getClickedBlock(); + if (block == null) { + try { block = p.getTargetBlockExact(5); } + catch (Exception ignored) {} + } + if (block == null) return; + UUID uid = p.getUniqueId(); + if (isLeft) { + pos1.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置1: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } else { + pos2.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置2: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } + } + return; + } + } + + // ===== 放置实体类物品时不检查(展示框、矿车、船、地图等)===== + Material mat = hand.getType(); + if (mat == Material.ITEM_FRAME + || mat == Material.GLOW_ITEM_FRAME + || mat == Material.MINECART + || mat == Material.TNT_MINECART + || mat == Material.CHEST_MINECART + || mat == Material.HOPPER_MINECART + || mat == Material.OAK_BOAT + || mat == Material.SPRUCE_BOAT + || mat == Material.BIRCH_BOAT + || mat == Material.JUNGLE_BOAT + || mat == Material.ACACIA_BOAT + || mat == Material.DARK_OAK_BOAT + || mat == Material.MANGROVE_BOAT + || mat == Material.CHERRY_BOAT + || mat == Material.BAMBOO_RAFT + || mat == Material.MAP + || mat == Material.ARMOR_STAND) { + return; + } + + // ===== 区域规则检查 ===== + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + + // 末影珍珠禁止 + if (ac.denyEnderPearl + && hand.getType() == Material.ENDER_PEARL) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用末影珍珠"); + return; + } + + // 弓箭禁止 + if (ac.denyBow + && (hand.getType() == Material.BOW + || hand.getType() == Material.CROSSBOW)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用弓箭"); + return; + } + + // 白名单跳过 + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 禁止使用物品 + String typeId = hand.getType().name(); + if (ac.denyUseItems.contains(typeId)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用此物品"); + return; + } + + // 没收检查 + handleConfiscate(p, ac); + } + + + + @EventHandler + public void onExplosion(EntityExplodeEvent e) { + Iterator it = e.blockList().iterator(); + while (it.hasNext()) { + Block b = it.next(); + AreaConfig ac = getArea( + b.getWorld().getName(), + b.getX(), b.getY(), b.getZ()); + if (ac != null && ac.denyExplosion) + it.remove(); + } + } + public void loadWhitelists() { + globalPlayerWhitelist.clear(); + areaPlayerWhitelist.clear(); + + if (!whitelistDir.exists()) { + whitelistDir.mkdirs(); + return; + } + + try { + // 加载全局白名单 + if (globalWhitelistFile.exists()) { + List lines = + java.nio.file.Files.readAllLines( + globalWhitelistFile.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + globalPlayerWhitelist.add(name); + } + } + } + + // 加载各区域白名单 + File[] files = whitelistDir.listFiles( + (File d, String n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (files != null) { + for (File f : files) { + String areaName = f.getName() + .replace(".txt", ""); + Set set = + ConcurrentHashMap.newKeySet(); + List lines = + java.nio.file.Files.readAllLines( + f.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + set.add(name); + } + } + if (!set.isEmpty()) { + areaPlayerWhitelist.put( + areaName, set); + } + } + } + + plugin.getLogger().info( + "[防护] 白名单加载完成,全局: " + + globalPlayerWhitelist.size() + + ",区域数: " + + areaPlayerWhitelist.size()); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载白名单失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onPotionSplash(PotionSplashEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + AreaConfig ac = getArea( + shooter.getWorld().getName(), + shooter.getLocation().getBlockX(), + shooter.getLocation().getBlockY(), + shooter.getLocation().getBlockZ()); + if (ac != null && ac.denyPotion + && !isPlayerWhitelisted(shooter.getName(), ac)) { + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止使用药水"); + } + } + + /** + * 解析区域名:先精准匹配,再模糊匹配 + * @param input 用户输入的区域名 + * @return 匹配到的区域名,无匹配返回null + */ + public String resolveAreaName(String input) { + if (input == null || input.isEmpty()) return null; + if (input.equalsIgnoreCase("global")) return "global"; + + // 第一步:精准匹配 + for (String name : areas.keySet()) { + if (name.equalsIgnoreCase(input)) { + return name; + } + } + + // 第二步:包含匹配(用户只打了部分名字) + for (String name : areas.keySet()) { + if (name.contains(input) || input.contains(name)) { + return name; + } + } + + // 第三步:编辑距离模糊匹配(著称→主城) + String bestMatch = null; + int bestDistance = Integer.MAX_VALUE; + for (String name : areas.keySet()) { + int dist = levenshteinDistance( + input.toLowerCase(), name.toLowerCase()); + if (dist < bestDistance) { + bestDistance = dist; + bestMatch = name; + } + } + + // 容错阈值:编辑距离 <= 名字长度的一半 + if (bestMatch != null) { + int threshold = Math.max(1, bestMatch.length() / 2); + if (bestDistance <= threshold) { + return bestMatch; + } + } + + return null; + } + + /** + * 判断字符串是否是区域名(精准或模糊) + */ + private boolean isAreaName(String name) { + if (name.equalsIgnoreCase("global")) return false; + return resolveAreaName(name) != null; + } + + /** + * 获取模糊匹配的实际区域名(用于提示用户) + */ + private String getResolvedName(String input) { + return resolveAreaName(input); + } + + /** + * 编辑距离算法(莱文斯坦距离) + */ + private int levenshteinDistance(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + for (int i = 0; i <= s1.length(); i++) dp[i][0] = i; + for (int j = 0; j <= s2.length(); j++) dp[0][j] = j; + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], + Math.min(dp[i - 1][j], dp[i][j - 1])); + } + } + } + return dp[s1.length()][s2.length()]; + } + + /** + * 智能重排参数:找出子命令位置,重排为标准顺序 + * 标准顺序:/protect 子命令 参数1 参数2 ... + */ + private String[] smartReorderArgs(String[] original) { + if (original.length <= 1) return original; + + Set knownSubs = new HashSet<>(Arrays.asList( + "add", "remove", "additem", "removeitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite", + "list", "listitem", "创建", "删除", "重载", + "工具", "expand", "contraction", + "on", "off", "tempon", "modeexempt" + )); + + String first = original[0].toLowerCase(); + if (knownSubs.contains(first)) { + return original; + } + + int subIndex = -1; + for (int i = 0; i < original.length; i++) { + if (knownSubs.contains(original[i].toLowerCase())) { + subIndex = i; + break; + } + } + + if (subIndex < 0) { + return original; + } + + String[] reordered = new String[original.length]; + reordered[0] = original[subIndex]; + int idx = 1; + for (int i = 0; i < original.length; i++) { + if (i == subIndex) continue; + reordered[idx] = original[i]; + idx++; + } + + return reordered; + } + + // ==================== 管理命令 ==================== + public boolean handleCommand(CommandSender sender, + String[] args) { + args = smartReorderArgs(args); + if (args.length == 0) { + showHelp(sender); + return true; + } + String sub = args[0].toLowerCase(); +// ===== 诊断命令 ===== + if (sub.equals("sdf1debug")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + sender.sendMessage("§e===== 调试信息 ====="); + sender.sendMessage("§7当前区域: " + + playerCurrentArea.get(uid)); + sender.sendMessage("§7标记效果数: " + + (playerMarkedEffects.containsKey(uid) + ? playerMarkedEffects.get(uid).size() + : "无标记")); + sender.sendMessage("§7待清理任务: " + + pendingClearTask.containsKey(uid)); + sender.sendMessage("§7区域数: " + areas.size()); + + if (playerMarkedEffects.containsKey(uid)) { + List marked = + playerMarkedEffects.get(uid); + for (PotionEffectType type : marked) { + sender.sendMessage("§7 标记: " + + type.getName() + + " 身上有=" + + (p.getPotionEffect(type) != null)); + } + } + return true; + } + +// ===== 强制清理命令(测试用)===== + if (sub.equals("testclear")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + List marked = + playerMarkedEffects.remove(uid); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + sender.sendMessage("§a已强制清理 " + + marked.size() + " 个效果"); + } else { + sender.sendMessage("§c无标记效果可清理"); + } + + // 取消延时任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + sender.sendMessage("§a已取消延时任务"); + } + + removePlayerEffects(uid, + playerCurrentArea.getOrDefault(uid, "")); + sender.sendMessage("§aDB已清理"); + return true; + } + + // ===== 工具 ===== + if (sub.equals("工具") || sub.equals("wand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + ItemStack tool = new ItemStack(WAND); + ItemMeta meta = tool.getItemMeta(); + meta.setDisplayName("§a§l区域选择工具"); + meta.setLore(Arrays.asList( + "§7左键: 位置1", "§7右键: 位置2")); + tool.setItemMeta(meta); + p.getInventory().addItem(tool); + p.sendMessage("§a§l[防护] §f已获取选择工具"); + return true; + } + + // ===== 创建 ===== + if (sub.equals("创建") || sub.equals("create")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 创建 <名>"); + return true; + } + Player p = (Player) sender; + UUID u = p.getUniqueId(); + if (!pos1.containsKey(u) + || !pos2.containsKey(u)) { + p.sendMessage("§c请先用工具选点"); + return true; + } + String areaName = args[1]; + File f = new File(rootDir, areaName + ".txt"); + if (f.exists()) { + p.sendMessage("§c区域已存在"); + return true; + } + Location l1 = pos1.get(u); + Location l2 = pos2.get(u); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 区域: " + areaName); + pw.println("起点: " + l1.getWorld().getName() + + "," + l1.getBlockX() + + "," + l1.getBlockZ()); + pw.println("终点: " + l2.getWorld().getName() + + "," + l2.getBlockX() + + "," + l2.getBlockZ()); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("# 禁止放置方块"); + pw.println("# 禁止破坏方块"); + pw.println("# 禁止PVP"); + pw.println("# 效果: 夜视 1 999"); + pw.println("# 进入提示: 欢迎来到保护区"); + pw.println("# 离开提示: 已离开保护区"); + pw.close(); + p.sendMessage("§a§l[防护] §f区域 " + + areaName + " 已创建"); + loadAllAreas(); + } catch (IOException ex) { + p.sendMessage("§c创建失败"); + } + return true; + } + + // ===== 和平白名单 ===== + if (sub.equals("addname")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别: " + + args[1] + " 或 " + args[2]); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 和平白名单"); + return true; + } + sender.sendMessage("§c用法: /protect addname " + + "<区域> <名字>"); + return true; + } + if (sub.equals("removename")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + parsed[0]); + return true; + } + sender.sendMessage("§c用法: /protect removename " + + "<区域> <名字>"); + return true; + } + if (sub.equals("listname")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listname <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 和平白名单:"); + if (ac.peaceWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.peaceWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 模式排除 ===== + if (sub.equals("addwhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect addwhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("removewhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect removewhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("listwhite")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listwhite <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 模式排除名单:"); + if (ac.modeExempt.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.modeExempt) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 列表 ===== + if (sub.equals("列表") || sub.equals("list")) { + if (args.length == 2) { + if (args[1].equalsIgnoreCase("global")) { + sender.sendMessage("§a全局白名单:"); + if (globalPlayerWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n + : globalPlayerWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + String an = args[1]; + Set wl = + areaPlayerWhitelist.get(an); + sender.sendMessage("§a" + an + " 白名单:"); + if (wl == null || wl.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : wl) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + if (areas.isEmpty()) { + sender.sendMessage("§7暂无区域"); + } else { + sender.sendMessage("§e§l区域列表:"); + for (Map.Entry en + : areas.entrySet()) { + sender.sendMessage("§a " + en.getKey() + + " §7规则:" + + en.getValue().ruleCount() + + "条"); + } + } + return true; + } + + // ===== 重载 ===== + if (sub.equals("重载") || sub.equals("reload")) { + reload(); + sender.sendMessage("§a已重载 " + + areas.size() + " 个区域"); + return true; + } + + // ===== 物品黑名单 ===== + if (sub.equals("additem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + args[1]); + return true; + } + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + itemName); + return true; + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + } else { + areaItemBlacklist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()) + .add(itemName); + sender.sendMessage("§a已加黑: " + + itemName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect additem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("removeitem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (!globalItemBlacklist + .contains(itemName)) { + sender.sendMessage( + "§c全局黑名单中没有: " + + itemName); + return true; + } + globalItemBlacklist.remove(itemName); + sender.sendMessage("§a已移除: " + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.remove(itemName); + } else { + Set list = + areaItemBlacklist.get(areaName); + if (list != null) { + list.remove(itemName); + } + } + sender.sendMessage("§a已移除: " + + itemName); + return true; + } + sender.sendMessage("§c用法: /protect removeitem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("listitem")) { + if (args.length < 2) { + sender.sendMessage( + "§a全局物品黑名单:"); + if (globalItemBlacklist.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item + : globalItemBlacklist) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + Set list = + areaItemBlacklist.get(args[1]); + sender.sendMessage("§a" + args[1] + + " 物品黑名单:"); + if (list == null || list.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item : list) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + + // ===== expand 扩建 ===== + if (sub.equals("expand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ -= amount; + else if (dir.equals("南")) maxZ += amount; + else if (dir.equals("东")) maxX += amount; + else if (dir.equals("西")) minX -= amount; + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f扩建 §e" + + amount + "§f格"); + return true; + } + + // ===== contract 收缩 ===== + if (sub.equals("contraction") + || sub.equals("contract")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ += amount; + else if (dir.equals("南")) maxZ -= amount; + else if (dir.equals("东")) maxX -= amount; + else if (dir.equals("西")) minX += amount; + if (minX > maxX || minZ > maxZ) { + p.sendMessage("§c收缩过度"); + return true; + } + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f收缩 §e" + + amount + "§f格"); + return true; + } + + // ===== 删除 ===== + if (sub.equals("删除") || sub.equals("delete")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 删除 <名>"); + return true; + } + File f = new File(rootDir, args[1] + ".txt"); + if (f.exists()) { + f.delete(); + areas.remove(args[1]); + sender.sendMessage("§a已删除: " + args[1]); + } else { + sender.sendMessage("§c不存在: " + args[1]); + } + return true; + } + + // ===== add 加白名单 ===== + if (sub.equals("add")) { + if (args.length == 2) { + String name = args[1]; + if (globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 已在全局白名单中"); + return true; + } + globalPlayerWhitelist.add(name); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + + playerName); + } else { + Set wl = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()); + wl.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已加白: " + + playerName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect add " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== remove 移除白名单 ===== + if (sub.equals("remove")) { + if (args.length == 2) { + String name = args[1]; + if (!globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 不在全局白名单中"); + return true; + } + globalPlayerWhitelist.remove(name); + saveWhitelists(); + sender.sendMessage("§a已从全局白名单移除: " + + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.remove(playerName); + saveWhitelists(); + sender.sendMessage("§a已移除: " + + playerName); + } else { + Set wl = + areaPlayerWhitelist.get(areaName); + if (wl != null) { + wl.remove(playerName); + } + saveWhitelists(); + sender.sendMessage("§a已从 " + areaName + + " 移除: " + playerName); + } + return true; + } + sender.sendMessage("§c用法: /protect remove " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== on/off/tempon 边框 ===== + if (sub.equals("on")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, false); + p.sendMessage("§a§l[防护] §f已显示 " + + ac.name + " 边框"); + return true; + } + if (sub.equals("off")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + cancelBorder((Player) sender); + ((Player) sender).sendMessage( + "§a§l[防护] §f已关闭边框显示"); + return true; + } + if (sub.equals("tempon")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, true); + p.sendMessage("§a§l[防护] §f临时显示 " + + ac.name + " 边框(15秒)"); + return true; + } + + showHelp(sender); + return true; + } + + + private String[] parseAreaAndTarget(String arg1, String arg2) { + String r1 = resolveAreaName(arg1); + if (r1 != null) { + return new String[]{r1, arg2}; + } + String r2 = resolveAreaName(arg2); + if (r2 != null) { + return new String[]{r2, arg1}; + } + return null; + } + + private boolean playerExists(String playerName) { + // 在线玩家 + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.getName().equalsIgnoreCase(playerName)) return true; + } + // 离线玩家 + @SuppressWarnings("deprecation") + OfflinePlayer op = Bukkit.getOfflinePlayer(playerName); + return op.hasPlayedBefore(); + } + + + private boolean validateArea(CommandSender sender, String areaName) { + if (!areas.containsKey(areaName)) { + sender.sendMessage("§c区域不存在: " + areaName); + return false; + } + return true; + } + + + private String matname(Material mat) { + String name = mat.name(); + String[] parts = name.split("_"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (sb.length() > 0) sb.append(" "); + sb.append(part.substring(0, 1).toUpperCase()) + .append(part.substring(1).toLowerCase()); + } + return sb.toString(); + } + private String yawToDir(float yaw) { + // MC中: 0=南, 90=西, 180=北, -90/270=东 + yaw = yaw % 360; + if (yaw < 0) yaw += 360; + if (yaw >= 315 || yaw < 45) return "南"; + if (yaw >= 45 && yaw < 135) return "西"; + if (yaw >= 135 && yaw < 225) return "北"; + return "东"; + } + private void saveAreaConfig(AreaConfig ac) { + File f = new File(rootDir, ac.name + ".txt"); + if (!f.exists()) return; + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + if (line.trim().startsWith("起点:")) { + lines.add("起点: " + ac.world + + "," + ac.x1 + "," + ac.z1); + } else if (line.trim().startsWith("终点:")) { + lines.add("终点: " + ac.world + + "," + ac.x2 + "," + ac.z2); + } else { + lines.add(line); + } + } + br.close(); + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + for (String l : lines) pw.println(l); + pw.close(); + } catch (IOException ignored) {} + } + + private void showHelp(CommandSender s) { + s.sendMessage("§e§l==== 区域防护 ===="); + s.sendMessage("§a/protect 工具 §7选地工具"); + s.sendMessage("§a/protect 创建 <名> §7创建区域"); + s.sendMessage("§a/protect 列表 §7区域列表"); + s.sendMessage("§a/protect 重载 §7重载配置"); + s.sendMessage("§a/protect 删除 <名> §7删除区域"); + s.sendMessage("§a/protect add [区域] <玩家> §7玩家加白"); + s.sendMessage("§a/protect remove [区域] <玩家> §7玩家删白"); + s.sendMessage("§a/protect additem [区域] <物品> §7物品加黑"); + s.sendMessage("§a/protect removeitem [区域] <物品> §7物品删黑"); + s.sendMessage("§a/protect on §7显示边框"); + s.sendMessage("§a/protect off §7关闭边框"); + s.sendMessage("§a/protect tempon §7临时边框(15秒)"); + s.sendMessage("§a/protect list §7列出全局白名单"); + s.sendMessage("§a/protect list <区域> §7列出区域白名单"); + s.sendMessage("§a/protect listitem §7列出全局物品黑名单"); + s.sendMessage("§a/protect listitem <区域> §7列出区域物品黑名单"); + + } +// ==================== 白名单管理公共方法 ==================== + + // ==================== 白名单操作 ==================== + + public boolean isPlayerGlobalWhitelisted( + String playerName) { + return globalPlayerWhitelist.contains(playerName); + } + + public boolean addPlayerToGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean isPlayerAreaWhitelisted( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + return wl != null && wl.contains(playerName); + } + + public boolean addPlayerToAreaWhitelist( + String areaName, String playerName) { + Set set = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap.newKeySet()); + boolean ok = set.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromAreaWhitelist( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + boolean ok = wl != null && wl.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + + + public int handleConfiscate(Player p, AreaConfig ac) { + if (isPlayerWhitelisted(p.getName(), ac)) return 0; + Set allItems = new HashSet<>(ac.confiscateItems); + allItems.addAll(globalItemBlacklist); + Set areaList = areaItemBlacklist.get(ac.name); + if (areaList != null) allItems.addAll(areaList); + + if (allItems.isEmpty()) return 0; + + int totalRemoved = 0; + String confiscatedNames = ""; + + for (String itemId : allItems) { + // 过滤掉区域名和无效ID + Material mat = Material.matchMaterial(itemId); + if (mat == null) { + plugin.getLogger().warning( + "[防护] 跳过无效物品: " + itemId); + continue; + } + ItemStack[] all = p.getInventory().getContents(); + int count = 0; + for (int i = 0; i < all.length; i++) { + if (all[i] != null && all[i].getType() == mat) { + count += all[i].getAmount(); + all[i] = null; + } + } + p.getInventory().setContents(all); + + ItemStack off = p.getInventory().getItemInOffHand(); + if (off != null && off.getType() == mat) { + count += off.getAmount(); + p.getInventory().setItemInOffHand(null); + } + + if (count > 0) { + totalRemoved += count; + if (!confiscatedNames.isEmpty()) + confiscatedNames += "§7、§e"; + confiscatedNames += "§e" + matname(mat) + + " x" + count; + } + } + + if (totalRemoved > 0) { + String msg = (ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty()) + ? ac.confiscateMsg + : "你携带了违禁品,已被没收"; + p.sendMessage("§c§l[区域防护] §f" + msg); + p.sendMessage("§7没收物品: " + confiscatedNames); + + if (ac.enableAnnounce + && ac.announceTemplate != null + && !ac.announceTemplate.isEmpty()) { + String ann = ac.announceTemplate + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)) + .replace("{items}", confiscatedNames); + Bukkit.broadcastMessage("§c§l[区域防护] §f" + ann); + } + for (String cmd : ac.punishCommands) { + String finalCmd = cmd + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)); + Bukkit.getScheduler().runTask(plugin, + () -> Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), finalCmd)); + } + } + return totalRemoved; + } + public void clearBlacklists() { + globalItemBlacklist.clear(); + areaItemBlacklist.clear(); + plugin.getLogger().info("[防护] 已清空所有黑名单"); + } + + @EventHandler + public void onEntityDamage( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑 ===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + // 展示框被点击(旋转/放入/取出) + @EventHandler + public void onItemFrameInteract( + PlayerInteractAtEntityEvent e) { + Entity clicked = e.getRightClicked(); + String typeName = clicked.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + Player p = e.getPlayer(); + + // 全局白放行 + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(clicked); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + // 非白名单 → 拦截 + // 记录当前旋转角度 + final Rotation savedRotation; + final Entity frame = clicked; + if (clicked instanceof org.bukkit.entity.ItemFrame) { + savedRotation = + ((org.bukkit.entity.ItemFrame) clicked) + .getRotation(); + } else { + savedRotation = null; + } + + e.setCancelled(true); + + // 下一tick强制恢复旋转 + if (savedRotation != null) { + final Rotation restore = savedRotation; + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (frame.isValid() + && frame instanceof org.bukkit.entity.ItemFrame) { + ((org.bukkit.entity.ItemFrame) frame) + .setRotation(restore); + } + }, 1L); + } + + p.sendMessage("§c§l[区域防护] §f禁止交互展示框"); + + // 驱逐远程敌对生物 + banRangedHostiles(p, ac); + } + + + + // 展示框保护:检查展示框本身+周围方块所属区域 + + // ===== 展示框攻击拦截(左键/投射物破坏)===== + @EventHandler + public void onEntityDamageByEntity( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + // 展示框被攻击 + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑(原有的,不动)===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + private AreaConfig findFrameArea(Entity frame) { + Location loc = frame.getLocation(); + int bx = loc.getBlockX(); + int by = loc.getBlockY(); + int bz = loc.getBlockZ(); + String world = loc.getWorld().getName(); + + AreaConfig ac = getArea(world, bx, by, bz); + if (ac != null && ac.denyItemFrame) return ac; + + int[][] offsets = { + {0, 0, 1}, {0, 0, -1}, + {1, 0, 0}, {-1, 0, 0}, + {0, 1, 0}, {0, -1, 0} + }; + for (int[] off : offsets) { + ac = getArea(world, + bx + off[0], by + off[1], bz + off[2]); + if (ac != null && ac.denyItemFrame) return ac; + } + return null; + } + + // ===== 投射物击中展示框(弓箭/弩)===== + @EventHandler + public void onProjectileHit( + org.bukkit.event.entity.ProjectileHitEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + Entity hit = e.getHitEntity(); + if (hit == null) return; + String typeName = hit.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(shooter.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(hit); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(shooter.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + } + + + // ===== 区域外火焰不能蔓延进区域 ===== + @EventHandler + public void onBlockSpread(BlockSpreadEvent e) { + Block block = e.getBlock(); + AreaConfig tgtArea = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (tgtArea != null && tgtArea.denyFireSpread) { + e.setCancelled(true); + } + } + + // ===== 区域内禁止点燃方块 ===== + @EventHandler + public void onIgnite(BlockIgniteEvent e) { + Player p = e.getPlayer(); + if (p == null) return; // 非玩家点燃的(闪电等) + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f此区域禁止点燃物品"); + } + } + + // ===== 区域内方块着火(TNT、岩浆等)也阻止 ===== + @EventHandler + public void onBlockBurn(BlockBurnEvent e) { + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) + e.setCancelled(true); + } + + + private void savePeaceWhitelist(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("和平") + && trimmed.contains("白名单")) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.peaceWhitelist.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存和平白名单失败: " + e.getMessage()); + } + } + + + @EventHandler + public void onPaintingPlace( + org.bukkit.event.player.PlayerBucketEmptyEvent e) { + // 暂不处理 + } + + + // ==================== 数据类 ==================== + + public static class AreaConfig { + public String name = ""; + public String world = ""; + public int x1, z1, x2, z2; + public int yMin = 0, yMax = 255; + public List confiscateItems = new ArrayList<>(); + public List denyUseItems = new ArrayList<>(); + public List clearEffects = new ArrayList<>(); // 清除单个效果名 + public boolean clearAllBadEffects = false; // 清除所有负面+中性 + public List giveEffects = new ArrayList<>(); // [效果名, 等级, 秒数] + public List punishCommands = new ArrayList<>(); + public boolean denyBlockPlace = false; + public boolean denyBlockBreak = false; + public boolean denyPVP = false; + public boolean denyFallDamage = false; + public boolean denyHunger = false; + public boolean denyAllDamage = false; + public boolean denyMount = false; + public boolean denyDrop = false; + public boolean denyEnderPearl = false; + public boolean denyBow = false; + public boolean denyPotion = false; + public boolean denyExplosion = false; + public boolean enableAnnounce = false; + public String announceTemplate = ""; + public String confiscateMsg = ""; + public String enterMsg = ""; + public String leaveMsg = ""; + public boolean denyRaid = false; + public boolean denyFireSpread = false; + public boolean denyAllEffects = false;; + public boolean peaceMode = false; + public boolean denyItemFrame = false; + public Set peaceWhitelist = new HashSet<>(); + public String enforceGameMode = null; // 强制游戏模式 + public Set modeExempt = new HashSet<>(); // 模式排除名单 + + + + + private static final Map EFFECT_MAP + = new HashMap<>(); + static { + // 负面效果 + EFFECT_MAP.put("缓慢", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("凋零", PotionEffectType.WITHER); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("霉运", PotionEffectType.UNLUCK); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("蓄风", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生", PotionEffectType.INFESTED); + // 中性效果 + EFFECT_MAP.put("不祥之兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("不祥征兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("袭击征兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("试炼征兆", PotionEffectType.TRIAL_OMEN); + // 正面效果 + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); + EFFECT_MAP.put("瞬间治疗", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("跳跃提升", PotionEffectType.JUMP_BOOST); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性提升", PotionEffectType.RESISTANCE); + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("生命提升", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("村庄英雄", PotionEffectType.HERO_OF_THE_VILLAGE); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); + EFFECT_MAP.put("潮涌能量", PotionEffectType.CONDUIT_POWER); + EFFECT_MAP.put("海豚的恩惠", PotionEffectType.DOLPHINS_GRACE); + + + // 带效果二字 + // 补充带"效果"后缀的别名 + EFFECT_MAP.put("黑暗效果", PotionEffectType.DARKNESS); + EFFECT_MAP.put("不祥之兆效果", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆效果", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆效果", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("缓慢效果", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("中毒效果", PotionEffectType.POISON); + EFFECT_MAP.put("凋零效果", PotionEffectType.WITHER); + EFFECT_MAP.put("失明效果", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿效果", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱效果", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("反胃效果", PotionEffectType.NAUSEA); + EFFECT_MAP.put("飘浮效果", PotionEffectType.LEVITATION); + EFFECT_MAP.put("发光效果", PotionEffectType.GLOWING); + EFFECT_MAP.put("霉运效果", PotionEffectType.UNLUCK); + EFFECT_MAP.put("蓄风效果", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝效果", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆效果", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生效果", PotionEffectType.INFESTED); + EFFECT_MAP.put("挖掘疲劳效果", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害效果", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("村庄英雄效果", PotionEffectType.HERO_OF_THE_VILLAGE); + } + + + public int ruleCount() { + int c = 0; + if (!confiscateItems.isEmpty()) c++; + if (denyBlockPlace) c++; + if (denyBlockBreak) c++; + if (denyPVP) c++; + if (denyFallDamage) c++; + if (denyHunger) c++; + if (denyAllDamage) c++; + if (denyDrop) c++; + if (denyMount) c++; + if (denyEnderPearl) c++; + if (denyBow) c++; + if (denyRaid) c++; + if (denyPotion) c++; + if (denyExplosion) c++; + if (denyFireSpread) c++; + if (denyAllEffects) c++; + if (!clearEffects.isEmpty()) c++; + if (clearAllBadEffects) c++; + if (!giveEffects.isEmpty()) c++; + if (enableAnnounce) c++; + if (!punishCommands.isEmpty()) c++; + if (!denyUseItems.isEmpty()) c++; + if (peaceMode) c++; + if (denyItemFrame) c++; + if (!peaceWhitelist.isEmpty()) c++; + + return c; + } + } + } \ No newline at end of file diff --git a/backup/20260605_152529/README.md b/backup/20260605_152529/README.md new file mode 100644 index 0000000..e74ef6a --- /dev/null +++ b/backup/20260605_152529/README.md @@ -0,0 +1,569 @@ +# Sdf1系列登录插件使用说明 + +# 特别说明:本插件仅有简中语言版本,如需除简中以外的任何版本,请下载插件源码,自行修改! + +## 1. 功能介绍 + +| 名字 | 描述/作用 | 施工状态 | +| :--- | :--- | :--- | +| 登录 | 用户登录/注册 | 已完成 | +| 世界垃圾箱 | 清理服务器凋落物 | 已完成 | +| 工单系统 | 用户提交工单 | 已完成 | +| 签到系统 | 签到积分换奖励 | 已完成 | +| 新人礼包 | 新玩家7日礼包 | 已完成 | +| 挂机检测 | 挂机检测踢出 | 已完成 | +| 自定义菜单 | 可以自己diy指令 | 已完成 | +| 自定义菜单图标 | 可以用自己喜欢的物品当图标 | 已完工 | +| 商城系统| 内置游戏商人可供销售商品 | 已完工 | +| 虚拟货币 | 供签到、游戏商人使用的货币系统 | 已完工 | + + +## 2.安装说明: + +| 前置插件 | 选装类型 | 功能作用 | +| :--- | :--- | :--- | +| [CY_beibao](https://gitee.com/nihaoshidifu/CY_beibao) | 可选,推荐 | 云背包,积分商城可购买背包容量 +| EssentialsX | 可选 | 主流经济系统 +| [sdf1](https://gitee.com/nihaoshidifu/sdf1_plugn) | 可选,推荐 | CDK兑换系统 + + +### 2.1 下载 +您可以从GitHub和Gitee下载到插件,将其丢入服务器plugin目录,重启服务器即可。 + +### 2.2. 配置文件 + +#### 2.2.1 消息.txt +此文件控制着整个插件的所有提示消息,您可以根据自己的需求修改,修改后需要执行 /sdf1_login reload 重载插件。支持[颜色代码](https://zh.minecraft.wiki/w/%E6%A0%BC%E5%BC%8F%E5%8C%96%E4%BB%A3%E7%A0%81)
+同时兼容` `标签,对段落加粗,`\n 和
`进行段落换行 + + +```txt +# ========================================== +# Sdf1_login 消息配置文件 +# ========================================== +# 颜色代码使用 § 符号 +# §0黑色 §1深蓝 §2深绿 §3深青 +# §4深红 §5深紫 §6金色 §7灰色 +# §8深灰 §9蓝色 §a绿色 §b浅蓝 +# §c红色 §d粉色 §e黄色 §f白色 +# §l粗体 §n下划线 §o斜体 §r重置 +# +# 占位符用 {xxx} 表示,运行时自动替换 +# 支持 粗体 标签 +# 支持 \n 或
换行 +# 修改后执行 /sdf1_login reload 重载 +# ========================================== + +# ---- 登录注册相关 ---- +# login_timeout: 登录超时被踢出时的提示 +login_timeout=§c登录超时,请重新登录 +# not_registered: 未注册玩家收到的提示 +not_registered=§e您尚未注册,请使用 /reg <密码> +# not_logged_in: 已注册但未登录的提示 +not_logged_in=§e您尚未登录,请使用 /login <密码> +# already_registered: 已注册玩家再次注册时的提示 +already_registered=§c您已注册过账号 +# already_logged_in: 已登录玩家再次登录时的提示 +already_logged_in=§c您已登录 +# reg_success: 注册成功提示 +reg_success=§a注册成功!欢迎来到服务器 +# login_success: 登录成功提示 +login_success=§a登录成功! +# login_failed: 密码错误提示 +login_failed=§c密码错误,请重试 + +# ---- 密码相关 ---- +# password_wrong: 旧密码验证失败 +password_wrong=§c密码错误 +# password_changed: 密码修改成功 +password_changed=§a密码修改成功! +# password_format_error: 密码格式不符合要求 +password_format_error=§c密码需6位以上,含大小写字母和数字 +# password_same: 新密码与旧密码相同 +password_same=§c新密码不能与旧密码相同 +# reset_no_email: 找回密码时未绑定邮箱 +reset_no_email=§c您未绑定邮箱,无法找回密码 +# reset_sent: 临时密码已发送到邮箱 +reset_sent=§a临时密码已发送到您的邮箱,请查收 + +# ---- 签到积分相关 ---- +# checkin_already: 今日已签到 +checkin_already=§c您今日已签到 +# checkin_success: 签到成功,{points}=获得积分 {streak}=连续天数 {multi}=倍率 +checkin_success=§a签到成功!获得 {points} 积分,连续 {streak} 天,倍率 x{multi} +# points_insufficient: 积分不足 +points_insufficient=§c积分不足,当前: {points} +# points_purchase_success: 积分购买成功 +points_purchase_success=§a购买成功!消耗 {count} 积分 + +# ---- 挂机检测相关 ---- +# afk_set_enabled: 挂机检测开启 +afk_set_enabled=§a挂机检测已开启 +# afk_set_disabled: 挂机检测关闭 +afk_set_disabled=§c挂机检测已关闭 +# afk_kick: 因挂机被踢出 +afk_kick=§c因挂机过久被踢出 + +# ---- 邮箱相关 ---- +# email_set: 邮箱设置成功 +email_set=§a邮箱设置成功:{email} +# email_format_error: 邮箱格式错误 +email_format_error=§c邮箱格式不正确 + +# ---- 邀请相关 ---- +# invite_code_generated: 邀请码生成 +invite_code_generated=§a邀请码:{code} +# invite_used: 使用邀请码成功 +invite_used=§a邀请成功,邀请人:{player} +# invite_self: 使用自己的邀请码 +invite_self=§c不能使用自己的邀请码 +# invite_not_found: 邀请码不存在 +invite_not_found=§c邀请码不存在 +# invite_referral_bonus: 被邀请人签到后邀请人获得奖励 +invite_referral_bonus=§a邀请奖励!获得 {points} 积分 + +# ---- 新人礼包相关 ---- +# gift_not_ready: 礼包条件未满足 +gift_not_ready=§c礼包条件未满足 +# gift_claimed: 礼包领取成功 +gift_claimed=§a礼包领取成功! + +# ---- 审批与IP限制 ---- +# need_approval: 注册需审批 +need_approval=§e注册需审批 (工单#{id}) +# ip_max_accounts: IP注册已达上限 +ip_max_accounts=§cIP注册已达上限 + +# ---- 管理员操作 ---- +# admin_delete_confirm: 管理员删除账号确认提示 +admin_delete_confirm=§c请在聊天中输入玩家名确认删除 + +# ---- 聊天过滤 ---- +# chat_muted: 禁言玩家尝试聊天 +chat_muted=§c§l[Sdf1_chat] §f你已被禁言,无法聊天 +# chat_url_blocked: 发送违规链接被拦截 +# {url}=检测到的链接地址 +chat_url_blocked=§c§l[Sdf1_chat] §f检测到违规第三方链接: §e{url} +# chat_url_violation: 当前违规次数 +# {count}=当前违规次数 +chat_url_violation=§7当前违规次数: §e{count} +# chat_url_admin_notify: 通知管理员,{player}=玩家名 {url}=链接 {count}=次数 +chat_url_admin_notify=§e§l[Sdf1_chat] §f{player} 发送违规链接: {url} (第{count}次) +# chat_url_broadcast: 全服通报,{player}=玩家名 +chat_url_broadcast=§c§l[Sdf1_chat] §e{player} §f因发送违规链接被处罚 + +# ---- 通用 ---- +# 未知参数: 未知命令或权限不足时的提示 +未知参数=§c未知参数或权限不足 +# only_player: 仅玩家可用的命令 +# only_player=§c此命令仅玩家可用 + +``` + +### 2.2.2 挂机白名单.txt +当挂机踢出开启时,该配置下的玩家不受挂机提出影响 + +### 2.2.3 SMTP设置.txt + +此文件控制着SMTP邮件服务器的配置,用于找回密码时发送邮件。
可通过GUI进行配置,也可以手动修改此配置文件。修改后,重载插件即可生效 + +```txt +# ===== SMTP 邮件服务器配置 ===== +smtp地址=smtp.example.com +smtp端口=465 +smtp账号=mail@example.com +smtp密码=example.com +发件人名称=Sdf1_login +smtp加密=true +验证码接收邮箱=example@example.com + +``` + +### 2.2.4 插件设置.txt + +此配置管控这整个插件,包括玩家能注册多少个账号、默认的管理员面板密码多少、tag包含什么视为管理员等 + +```txt +# ===== Sdf1_login 插件设置 ===== +管理标签=admin +管理密码=qweasd +每IP最大账号数=3 +审批模式=manual +自动审批延迟分钟=30 +登录超时秒数=60 +挂机踢出=false +挂机超时秒数=300 +服务商积分倍率=2.0 +基础经济奖励=100 +经济奖励每评分=50.0 +报单人积分奖励=10 +# ---- 签到盲盒奖励 ---- +签到固定金额=0 +签到最小金额=10 +签到最大金额=100 +签到给积分=true +签到积分数量=10 + + +``` + +### 2.2.5 chat.txt +该文件控制着聊天过滤,当玩家发送违规链接时,会自动根据配置规则进行处理。
+可用的处罚规则: +- warn: 警告 +- mute: 禁言 +- kick: 踢出 +- ban: 封禁 +- banip:封禁IP + +```txt +# Sdf1_chat 聊天过滤配置 +通知管理员: true +全服通报: true +禁言时长: 300 + +白名单: +*.minecraft.net +baidu.com +github.com + +处罚规则: +1:warn:0 +2:warn:0 +3:mute:300 +5:mute:600 +8:kick:0 +10:ban:0 + +白名单玩家: + +``` +### 2.2.6 radio_config.txt +```txt +# ============ Sdf1 Radio Config ============ +# +# resource-pack-url +# 填外部地址(HTTP或HTTPS均可) +# 例: https://example.com/radiopack.zip +# 留空 = 使用内置HTTP服务 +# +# http-port +# 内置HTTP端口(仅resource-pack-url为空时生效) +# +# ============ Settings =============== + +resource-pack-url=https://*.ypshidifu.cn/|*.mcserver.ccwu.cc +http-port=443 +``` +- radio_config这里可以使用通配符域名,当使用通配符时,系统随机生成url,拒绝过资源包的玩家,每次都会收到提示。并且自1.2版本起,拒绝资源包=放弃游玩服务器。如果您无需广播功能,可以不放ogg文件进入`./plugin/radio/ogg`。这样服务端不会自动生成资源包,也不会强制下载了 + +### 2.2.7 菜单.txt +- 自定义菜单配置文件 + +```json +# 服务器菜单 +# 使用类json格式 +// 支持使用://Java单行注释、#yml单行注释、/* */块注释,也支持的块注释 + +{ + 标题:主城传送 + 指令:/dom tp 主城区 # 这里如果是xxx.txt则表示进入二级目录 + 权限类型:玩家 + 图标:COMPASS +} + +{ + 标题:随机传送 + 指令:/rtp + 权限类型:玩家 + 图标:ENDER_PEARL +} + +{ + 标题:擂台比武 + 指令:/dom tp PVP + 权限类型:玩家 + 图标:netherite_spear +} + +{ + 标题:领地操作 + 指令:/dom + 权限类型:玩家 + 图标:paper +} +{ + 标题:逛商店 + 指令:/dom tp shop + 权限类型:玩家 + 图标:honeycomb +} + +{ + 标题:传送开关 + 指令:/tptoggle + 权限类型:玩家 + 图标:redstone_torch +} + +{ + 标题:返回上一个位置 + 指令:/back + 权限类型:玩家 + 图标:red_bed +} +{ + 标题:前往小黑塔 + 指令:/dom tp 小黑塔 + 权限类型:玩家 + 图标:dragon_head +} +{ + 标题:云背包 + 指令:/cy + 权限类型:玩家 + 图标:potato +} +{ + 标题:自杀 + 指令:/suicide + 权限类型:玩家 + 图标:wooden_axe +} +{ + 标题:游戏商人 + 指令:/shop + 权限类型:玩家 + 图标:shield + 格子:28 +} +``` +### 2.2.8 商品配置 +- 配置此处的文件,负责展示游戏内所销售的商品。
+- 一个文件名,对应一个商品分类,使用md文件格式 +``` +# 建筑材料 + +| ID | 品名 | 材质 | 购入价 | 售出价 | 库存 | 本小时销量 | 总销量 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| STONE | 石头 | STONE | 10 | -1 | -1 | 0 | 0 | +| COBBLESTONE | 圆石 | COBBLESTONE | 5 | -1 | -1 | 0 | 26 | +| OAK_PLANKS | 橡木木板 | OAK_PLANKS | 8 | 3 | -1 | 0 | 0 | +| BRICKS | 砖块 | BRICKS | 12 | -1 | -1 | 0 | 0 | +| GLASS | 玻璃 | GLASS | 15 | 6 | -1 | 0 | 0 | +| QUARTZ_BLOCK | 石英块 | QUARTZ_BLOCK | 20 | 8 | -1 | 0 | 0 | +| AMETHYST_BLOCK | 紫水晶 | AMETHYST_BLOCK | 10 | 0 | -1 | 0 | 999 | +| OCHRE_FROGLIGHT | 赭黄蛙鸣灯 | OCHRE_FROGLIGHT | 100 | 0 | -1 | 0 | 1000 | + +``` + +## 2.3 商城部分介绍 +1. 自助退款,买多买错,不用慌,一件退单很安心
+2. 购物车,买的多怕买错,先加购物车,后续统一结算
+3. 害怕订单找不到?一键查看历史订单
+ ![](img/shop4.png) +4. 玩家要耍赖皮,拿着小票来找麻烦怎么办?退款订单自动贴标,拒绝白嫖 +![](img/shop7.png)
+![](img/shop6.png) +5. 想查看玩家消费记录?小手一点,明明白白
+![](img/shop5.png) + + + + +## 2.4 打印流水部分介绍 +1. 管理员怀疑某个玩家刷钱了,怎么办?/printer 玩家名,一检查流水,很安心。很多人都刷了怎么办?/printer不加参数,一键导出全服流水,查他个底朝天
+2. 忘记删除怎么办?插件每分钟自动检测,旧文件超过24小时,自动删 + + + +## 3. 指令参数及介绍 + +- Sdf1_login 主指令
+- Sdf1_login reload 重载配置文件
+- reg 注册账号
+- login 登录账号
+- l 登录账号
+- sdf1_login del 删除玩家账号
+- sdf1_login radio 停止播放广播
+- sdf1_login radio reload 重载广播并重新打包资源包
+- menu 开/关 是否开启菜单
+- printer 打印流水数据,支持不带参数&带玩家名为参数
+- sdf1_login printer 作用同printer
+- sdf1_login shop 商城 +- protect 区域防护 + + + +## 4. 自定义菜单物品 +- 打开主界面,选择"我的"进入
+![](img/menu1.png) +- 将你的自定义物品与雪球交换
+![](img/menu2.png) +- 随后系统会给你重新发放菜单,并且此时图标就是你的自定义物品了 + +## 5. pvp战神榜 +- 通过此功能,你可以在服务器里制作大服务器同款的击杀排行榜,而且能精准定位指定区域
+![](img/pvp1.png) +- 并且有着网游同款的击杀通报
+![](img/pvp3.png) +- 播报逻辑是:
+- 3杀
+- 5杀
+- 5杀以上每杀一次都是一条播报
+- 终结者通报,触发连杀的玩家被终结也会有全服通报 + +## 6. 区域防护 +- 通过此功能,您可在服务器里需要防护的地方(比如展示框不想被玩家随便破坏、主城不准玩家袭击等)加以防护。
+- 还能够保护指定区域的玩家不受任何伤害。
+ +| 指令 | 参数1| 参数2 | 参数3 | 结果 | +| :---: | :---: | :---: | :---: | :---: | +| protect | 无 | 无 | 无 | 帮助信息 | +| protect | add | 区域名 | 玩家名 | 加白指定玩家到区域 | +| protect | add | 玩家名 | 无| 加白指定玩家为全局 | +| protect | additem | 区域名 | 物品名 | 加黑指定物品到区域 | +| protect | additem | 物品名 | 无 | 加黑指定物品到全局 | +| protect | addname | 区域名 | 名字 | 加白指定生物名字到区域和平模式白名单 | +| protect | addplayer | 区域名 | 玩家名字 | 加白指定玩家到区域强制模式豁免名单 | +| protect | remove | 玩家名字 | 无 | 移除玩家为全白 | +| protect | remove | 区域名 | 玩家名 | 移除玩家为区白 | +| protect | removeitem | 物品名 | 无 | 删除指定物品为全黑 +| protect | removeitem | 区域名 | 物品名 | 删除指定物品为区黑 +| protect | removename | 区域名 | 名字 | 删除指定名字为区域和平模式白名单 | +| protect | removeplayer | 区域名 | 名字 | 删除指定玩家为强制模式豁免名单 | +| protect | 参数不分顺序 | 区名可以写这里也可以写后面 | 玩家名可以写这里也可以写前面 | 完美的宝宝化 | + +示范: +- `/protect add 末地保护区 players` 添加players玩家到末地保护区
+- `/protect removeitem 主城区 TNT` 从主城删了TNT这个黑名单物品
+- `/protect 筑城区 addwhite zhangsan` 把zhangsan加白到主城区的模式豁免名单。
+- 所有参数`任意排序`,`区域名字`随便填写,强大的`兜底`会自动识别"宝宝画板",你可以放心、大胆的乱写 + + +| 参数 | 效果 | +| :---: | :---: | +| 和平模式 | 所有敌对生物全部进虚空 | +| 禁止袭击 | 立即清理玩家身上的袭击效果,传灾厄村民进虚空 | +| 清除XX效果 | 立即清除玩家身上的效果 +| 给与xx效果 | 根据配置文件,给予玩家指定等级、时长的效果 + +## 生物属性对照表 +| 中文名称 | 类型 | 附加属性 | +| :---: | :----: | :----: | +| 溺尸 | 近战/远程 | 亡灵、水生 | +| 骷髅 | 远程 | 亡灵 | +| 流浪者 | 远程 | 亡灵、寒冷群系变种 | +| 沼骸 | 远程 | 亡灵、沼泽变种 | +| 焦骸 | 远程 | 亡灵、沙漠变种、免疫阳光 | +| 掠夺者 | 远程 | 灾厄村民、袭击成员 | +| 卫道士 | 近战 | 灾厄村民、袭击成员 | +| 幻术师 | 远程 | 灾厄村民、Java专属、隐身分身 | +| 旋风人 | 远程 | 试炼密室生成、免疫弹射物、强击退 | +| 监守者 | 近战/声波远程 | 深暗之域、附带盲视效果 | +| 远古守卫者 | 近战/激光远程 | 海底神殿、水下、附加挖掘疲劳 | +| 守卫者 | 近战/激光远程 | 海底神殿、水下 | + + +配置文件解析:
+- 支持注释格式:`Java的//单行注释`、`yml的#单行注释`、`HTML的注释`和`Java/* */块注释` +```txt +# 区域: 主城区 +起点: world,-130,-713 +终点: world,-427,-560 +高度范围: 0-255 + +没收物品: 蘑菇,毒蘑菇 #没收哪些物品(末地推荐这两个) + // 禁止放置方块 #保护区内,非区白/全白玩家禁止放置方块 +// 禁止破坏方块 # 同上 +禁止PVP # 玩家之间不能PVP +禁止摔伤 # 摔落无法造成任何伤害 +禁止饥饿 # 不会消耗饱食度 +禁止一切伤害 # 任何怪物都无法造成伤害 +# 禁止丢弃物品 +# 禁止末影珍珠 +# 禁止使用弓箭 +# 禁止骑乘 +// 和平模式 // 所有的敌对生物全部传送进虚空 +禁止袭击 +禁止交互展示框 # 核心,任何玩家不能触碰展示框。区白/全白玩家除外 +// 清除所有效果 # 清理所有负面效果 +效果 村庄英雄 255 999999 # 给予玩家效果。格式为: 效果 效果名字 等级(最高255) 时间 +效果 生命提升 255 999999 +效果 抗性提升 255 999999 +效果 抗火 255 999999 +# 禁止药水 +禁止爆炸 +禁止使用物品: TNT +# 效果: 夜视 1 999 +进入提示: 欢迎来到保护区 # 是否开启进入提示,即自定义提示 +离开提示: 已离开保护区 + 没收提示: 你的违禁品已被没收 + 通报批评: §c§l{player} §f在{area}违规携带违禁品,被没收 §e{count}§f件! # 通报违禁品。{player} 玩家名 {count}数量 {area}区域 +# 惩罚命令: ban {player} 60s 违规 + + +``` + +## 运行截图 + +主界面 +![](img/login.png) + +邀请数据 +![](img/login2.png) + +积分商店 +![](img/login3.png) + +任务中心 +![](img/login4.png) + +垃圾箱 +![](img/login5.png) + +管理员面板 +![](img/login6.png) + +管理面板-用户管理 +![](img/login7.png) + + +工单中心 +![](img/login8.png) + +工单管理 +![](img/login9.png) + +我的工单 +![](img/login10.png) + + +提交工单 +![](img/login11.png) + +服务商列表 +![](img/login12.png) + +服务商抢单大厅 +![](img/login13.png) + +服务商-接单界面 +![](img/login14.png) + +管理员-工单详情页面 +![](img/login15.png) + +商城主界面 +![](img/shop1.png) + +商城购物车 +![](img/cart1.png) + +商城退单 +![](img/shop2.png) + + +## 4. 下载地址 +- GitHub:[https://github.com/ypsdf1/sdf1_login](https://github.com/ypsdf1/sdf1_login) +- Gitee:[https://gitee.com/nihaoshidifu/sdf1_login](https://gitee.com/nihaoshidifu/sdf1_login) diff --git a/backup/20260605_152529/pom.xml b/backup/20260605_152529/pom.xml new file mode 100644 index 0000000..9d5c185 --- /dev/null +++ b/backup/20260605_152529/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + sdf1 + Sdf1_login + 1.0 + jar + + + 21 + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + jitpack.io + https://jitpack.io + + + + + + io.papermc.paper + paper-api + 1.21.4-R0.1-SNAPSHOT + provided + + + + com.github.MilkBowl + VaultAPI + 1.7.1 + provided + + + org.xerial + sqlite-jdbc + 3.46.0.0 + provided + + + + + + Sdf1_login + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + + diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/AFKManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/AFKManager.java new file mode 100644 index 0000000..f714c1b --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/AFKManager.java @@ -0,0 +1,197 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +public class AFKManager { + + // 玩家最后活动时间(空闲才开始计时) + private final Map lastAction = + new HashMap<>(); + // 每个玩家当前的警告阶段 + private final Map lastStage = + new HashMap<>(); + // 是否已进入挂机检测(false=还在活动,不计时) + private final Map afkTracking = + new HashMap<>(); + + private final ConfigManager config; + private final JavaPlugin plugin; + + public AFKManager(ConfigManager config, + JavaPlugin plugin) { + this.config = config; + this.plugin = plugin; + } + + public void startCheck() { + new BukkitRunnable() { + public void run() { + tick(); + } + }.runTaskTimer(plugin, 20L, 20L); + } + + private void tick() { + if (!config.afkEnabled) return; + long now = System.currentTimeMillis(); + + for (Player p : Bukkit.getOnlinePlayers()) { + String name = p.getName(); + UUID uuid = p.getUniqueId(); + + if (!isLoggedIn(name)) continue; + if (config.getAfkWhitelist().contains(name)) + continue; + + // 确保有记录 + if (!lastAction.containsKey(uuid)) { + lastAction.put(uuid, now); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + long last = lastAction.get(uuid); + long idle = (now - last) / 1000; + int timeout = config.afkTimeout; + + // 还在活动范围内:不进入挂机检测 + if (idle < 60) { + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + // 超过60秒无活动:开始挂机检测 + afkTracking.put(uuid, true); + long remain = timeout - idle; + + // 已超时:踢出 + if (remain <= 0) { + p.kickPlayer("§c§l你已挂机 " + + fmtSec(idle) + ",被踢出"); + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + continue; + } + + int stage = lastStage.getOrDefault(uuid, 0); + + // 阶段3:最后10秒 + if (remain <= 10) { + if (stage < 3) { + lastStage.put(uuid, 3); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + remain + " 秒踢出", + 0, 22, 3); + playSound(p); + continue; + } + + // 阶段2:1分钟~10秒 + if (remain <= 60) { + if (stage < 2) { + lastStage.put(uuid, 2); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + fmtSec(remain) + " 踢出", + 5, 25, 5); + playSound(p); + continue; + } + + // 阶段1:3分钟提醒 + if (remain <= 180 && stage < 1) { + lastStage.put(uuid, 1); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§f还剩 " + fmtSec(remain) + " 踢出", + 10, 60, 20); + for (int i = 0; i < 3; i++) { + final int delay = i * 10; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + if (p.isOnline()) playSound(p); + }, delay); + } + } + + if (remain > 180) { + lastStage.remove(uuid); + } + } + } + + /** + * 玩家有活动时调用(移动/交互/放置/挖掘等) + * 重置计时,退出挂机检测 + */ + public void recordAction(UUID uuid) { + lastAction.put(uuid, System.currentTimeMillis()); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + } + + /** + * 玩家下线时调用 + * 彻底停止计时,清理所有数据 + */ + public void remove(UUID uuid) { + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + } + + /** + * 获取玩家当前是否在挂机检测中 + */ + public boolean isAfkTracking(UUID uuid) { + return afkTracking.getOrDefault(uuid, false); + } + + private boolean isLoggedIn(String name) { + return ((Main) plugin).getLoggedIn() + .contains(name); + } + + private void playSound(Player p) { + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + + private String fmtSec(long sec) { + if (sec < 60) return sec + "秒"; + long m = sec / 60; + long s = sec % 60; + return m + "分" + (s > 0 ? s + "秒" : ""); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/AccountRequestManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/AccountRequestManager.java new file mode 100644 index 0000000..ae5eaad --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/AccountRequestManager.java @@ -0,0 +1,146 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class AccountRequestManager { + + public enum ApprovalMode { AUTO, MANUAL, HYBRID } + + public static class Request { + public final int id; + public final String requester; + public final String target; + public final String ip; + public final long time; + public String status; + public String denyReason; + + public Request(int id, String requester, String target, String ip) { + this.id = id; + this.requester = requester; + this.target = target; + this.ip = ip; + this.time = System.currentTimeMillis(); + this.status = "PENDING"; + this.denyReason = ""; + } + } + + private final Main plugin; + private final Map requests = new ConcurrentHashMap<>(); + private final Map pendingByPlayer = new ConcurrentHashMap<>(); + private int nextId = 1; + private ApprovalMode mode = ApprovalMode.MANUAL; + private int autoDelayMinutes = 60; + + public AccountRequestManager(Main plugin) { + this.plugin = plugin; + } + + public ApprovalMode getMode() { return mode; } + + public void setMode(String m) { + try { this.mode = ApprovalMode.valueOf(m.toUpperCase()); } + catch (Exception e) { this.mode = ApprovalMode.MANUAL; } + } + + public void setAutoDelayMinutes(int min) { + this.autoDelayMinutes = min; + } + + public Request createRequest(String requester, String target, String ip) { + int id = nextId++; + Request req = new Request(id, requester, target, ip); + requests.put(id, req); + pendingByPlayer.put(requester, id); + + if (mode == ApprovalMode.AUTO) { + scheduleAutoApprove(id); + } else if (mode == ApprovalMode.HYBRID) { + String targetIp = (String) plugin.getDb().getField(target, "ip_address"); + if (ip != null && ip.equals(targetIp)) { + scheduleAutoApprove(id); + } + } + return req; + } + + private void scheduleAutoApprove(int id) { + long delayTicks = (long) autoDelayMinutes * 60L * 20L; + Request req = requests.get(id); + if (req == null) return; + String requesterName = req.requester; + String targetName = req.target; + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 将在 " + autoDelayMinutes + " 分钟后自动通过"); + + Bukkit.getScheduler().runTaskLater(plugin, () -> { + Request r = requests.get(id); + if (r != null && "PENDING".equals(r.status)) { + approveRequest(id); + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 自动通过"); + Player requester = Bukkit.getPlayer(requesterName); + if (requester != null && requester.isOnline()) { + requester.sendMessage("§a[Sdf1_login] §f您的删除请求 #" + id + "(§e" + targetName + "§a)已自动通过!"); + } + } + }, delayTicks); + } + + public boolean approveRequest(int id) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "APPROVED"; + plugin.getDb().deleteUser(req.target); + pendingByPlayer.remove(req.requester); + // 清背包+传送出生点+踢出 + Player target = Bukkit.getPlayer(req.target); + if (target != null && target.isOnline()) { + target.getInventory().clear(); + target.getInventory().setArmorContents(new org.bukkit.inventory.ItemStack[4]); + target.getInventory().setExtraContents(new org.bukkit.inventory.ItemStack[1]); + target.teleport(target.getWorld().getSpawnLocation()); + target.kickPlayer("§c§l你的账号已被管理员删除,请重新注册"); + } + return true; + } + + + public boolean denyRequest(int id, String reason) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "DENIED"; + req.denyReason = reason; + pendingByPlayer.remove(req.requester); + return true; + } + + public List getPendingRequests() { + List list = new ArrayList<>(); + for (Request r : requests.values()) { + if ("PENDING".equals(r.status)) list.add(r); + } + return list; + } + + public Request getRequest(int id) { + return requests.get(id); + } + + public boolean hasPending(String player) { + return pendingByPlayer.containsKey(player); + } + + public void cancelPending(String player) { + Integer id = pendingByPlayer.remove(player); + if (id != null) { + Request r = requests.get(id); + if (r != null) r.status = "CANCELLED"; + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/AreaProtection.java b/backup/20260605_152529/src/main/java/Sdf1_login/AreaProtection.java new file mode 100644 index 0000000..bc53d53 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/AreaProtection.java @@ -0,0 +1,4907 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import java.sql.*; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.GameMode; +import org.bukkit.event.EventPriority; + + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.entity.Item; + + + + +public class AreaProtection implements Listener { + + private final Main plugin; + public List giveEffects = new ArrayList<>(); + private final Map> playerAreas + = new HashMap<>(); + private final Map areas = + new ConcurrentHashMap<>(); + private Connection dbConnection; + + // 全局白名单(玩家) + private final Set globalPlayerWhitelist = + ConcurrentHashMap.newKeySet(); + // 区域白名单 key=区域名 value=玩家名集合 + private final Map> areaPlayerWhitelist = + new ConcurrentHashMap<>(); + // 全局黑名单(物品ID) + private final Set globalItemBlacklist = + ConcurrentHashMap.newKeySet(); + // 区域黑名单 + private final Map> areaItemBlacklist = + new ConcurrentHashMap<>(); + + // 选地 + private static final Material WAND = Material.BREEZE_ROD; + + private final Map pos1 = + new ConcurrentHashMap<>(); + private final Map pos2 = + new ConcurrentHashMap<>(); + private final Set selecting = + ConcurrentHashMap.newKeySet(); + public boolean denyRaid = false; + // ===== 统一文件夹 ===== + private final File rootDir; + private final File whitelistDir; + private final File globalWhitelistFile; + private final File oldDbFile; + private final File newDbFile; + + // 边框显示 + private final Map displayTaskIds = + new ConcurrentHashMap<>(); + private final Map> globalWhitelist + = new HashMap<>(); + private final Map> areaWhitelist + = new HashMap<>(); + private final Map protectedEntities + = new ConcurrentHashMap<>(); + // 区域给予的效果记录(贴标) + private final Map> playerAppliedEffects = new HashMap<>(); + // 延时清理任务 + private static final long MOVE_DEBOUNCE_MS = 100; + // 新:全部改为线程安全 + private final Map playerCurrentArea + = new ConcurrentHashMap<>(); + private final Map> playerMarkedEffects + = new ConcurrentHashMap<>(); + private final Map pendingClearTask + = new ConcurrentHashMap<>(); + private final Map pendingClearArea + = new ConcurrentHashMap<>(); + private final Map lastMoveProcess + = new ConcurrentHashMap<>(); + // 防止传送时重复处理 + private final Set teleporting + = ConcurrentHashMap.newKeySet(); + // 待清理的效果列表(与延时任务配合) + private final Map> pendingClearEffects + = new ConcurrentHashMap<>(); + + + +// ==================== 强制游戏模式 ==================== + + /** + * 监听玩家尝试切换游戏模式 + * 如果在强制游戏模式区域且不在豁免名单中,则取消切换 + */ + // 防止递归的标记 + private final Set modeSwitching + = ConcurrentHashMap.newKeySet(); + @EventHandler(priority = EventPriority.HIGHEST) + public void onGameModeChange( + PlayerGameModeChangeEvent event) { + if (event.isCancelled()) return; + + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + if (modeSwitching.contains(uid)) return; + + String areaName = playerCurrentArea.get(uid); + if (areaName == null) return; + + AreaConfig ac = areas.get(areaName); + if (ac == null) return; + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) return; + if (isExemptFromGameMode(p, areaName)) return; + + event.setCancelled(true); + + modeSwitching.add(uid); + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } finally { + modeSwitching.remove(uid); + } + + // ★ 切换后发一次中文提示 + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + ac.enforceGameMode + + " 模式,无法切换")); + } + + public Set getAlreadyForced() { + return alreadyForced; + } + + + // ==================== 定时强制游戏模式 ==================== + + private BukkitTask enforceTask; + + /** + * 启动定时强制任务 + * 每秒检查一次所有在线玩家 + */ + private BukkitTask enforceTask1; + + // 记录上次强制模式的玩家(避免重复发消息) + private final Set alreadyForced + = ConcurrentHashMap.newKeySet(); + + public void startEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + } + enforceTask = new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String areaName = + playerCurrentArea.get(uid); + if (areaName == null) { + // 不在任何区域,清除标记 + alreadyForced.remove(uid); + continue; + } + + AreaConfig ac = areas.get(areaName); + if (ac == null) continue; + if (ac.enforceGameMode == null + || ac.enforceGameMode + .isEmpty()) continue; + if (isExemptFromGameMode( + p, areaName)) { + alreadyForced.remove(uid); + continue; + } + + GameMode target = + GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + modeSwitching.add(uid); + try { + p.setGameMode(target); + } finally { + modeSwitching.remove(uid); + } + + // ★ 只在玩家已被强制过时才发消息 + // 首次不发,后续才提醒 + if (alreadyForced.contains(uid)) { + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + + ac.enforceGameMode + + " 模式,无法切换")); + } + alreadyForced.add(uid); + } else { + // 模式一致,清除标记 + alreadyForced.remove(uid); + } + } + } + }.runTaskTimer(plugin, 20L, 20L); + } + + public void stopEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + enforceTask = null; + } + } + + + + /** + * 检查玩家是否豁免于游戏模式强制 + * 豁免条件:全局白名单 / 区域白名单 / 模式排除名单 + */ + private boolean isExemptFromGameMode( + Player p, String areaName) { + String name = p.getName(); + + /* if (p.isOp()) { + plugin.getLogger().info( + "[防护-调试] 豁免原因: OP, 玩家=" + name); + return true; + }*/ + if (globalPlayerWhitelist.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 全局白名单, 玩家=" + + name);*/ + return true; + } + Set aw = + areaPlayerWhitelist.get(areaName); + if (aw != null && aw.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 区域白名单, 玩家=" + + name + " 区域=" + areaName);*/ + return true; + } + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 模式排除名单, 玩家=" + + name);*/ + return true; + } + /* plugin.getLogger().info( + "[防护-调试] 无豁免, 玩家=" + name);*/ + return false; + } + + /** + * 玩家进入区域时,如果该区域强制游戏模式, + * 则立即设置(排除豁免玩家) + */ + private void applyEnforcedGameMode(Player p, + String areaName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: ac=null, area=" + + areaName);*/ + return; + } + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: 未配置强制模式, area=" + + areaName);*/ + return; + } + + boolean exempt = isExemptFromGameMode( + p, areaName); + /* plugin.getLogger().info( + "[防护-调试] applyMode: 玩家=" + p.getName() + + " 区域=" + areaName + + " 强制=" + ac.enforceGameMode + + " 当前=" + p.getGameMode() + + " 豁免=" + exempt + + " OP=" + p.isOp() + + " 全白=" + globalPlayerWhitelist + .contains(p.getName()));*/ + + if (exempt) return; + + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + /* plugin.getLogger().info( + "[防护] 强制切换 " + p.getName() + + " → " + ac.enforceGameMode);*/ + } + } catch (Exception e) { + /* plugin.getLogger().warning( + "[防护] 强制模式失败: " + + e.getMessage());*/ + } + } + + public AreaProtection(Main plugin) { + this.plugin = plugin; + + // 统一根目录 + rootDir = new File(plugin.getDataFolder(), "区域防护"); + if (!rootDir.exists()) rootDir.mkdirs(); + + // 白名单子目录 + whitelistDir = new File(rootDir, "whitelists"); + if (!whitelistDir.exists()) whitelistDir.mkdirs(); + globalWhitelistFile = new File( + whitelistDir, "全局白名单.txt"); + + // 数据库迁移 + oldDbFile = new File( + plugin.getDataFolder(), "area_effects.db"); + newDbFile = new File(rootDir, "area_effects.db"); + migrateDatabase(); + + // 初始化 + initDatabase(); + writeDefaultConfig(); + loadAllAreas(); + loadWhitelists(); + recoverPendingEffects(); + } + public void recoverPendingEffects() { + try { + // 1. 遍历所有在线玩家(而不是遍历DB记录) + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 2. 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + // 3. 获取该玩家在DB中的所有历史记录 + List recordedAreas = + getPlayerAreas(uid); + + // 4. 清理不在当前区域的旧记录 + for (String recordedArea : recordedAreas) { + if (currentAc == null + || !currentAc.name + .equals(recordedArea)) { + // 玩家不在这个区域了 → 清DB + removePlayerEffects( + uid, recordedArea); + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在" + recordedArea + + ", 清理旧记录"); + } + } + + // 5. 如果在区域中 → 恢复/给予效果 + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } else { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在任何区域, 跳过"); + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 恢复效果失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + // 取消延时清理任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 清标记 + playerMarkedEffects.remove(uid); + playerCurrentArea.remove(uid); + } + + +// ==================== 数据库迁移 ==================== + + /** + * 如果旧位置有 db 文件,移到新位置 + * 如果新位置已有,不动 + * 如果旧位置也没有,在新位置创建 + */ + + private void migrateDatabase() { + if (newDbFile.exists()) { + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + oldDbFile.delete(); + } + return; + } + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + try { + java.nio.file.Files.move( + oldDbFile.toPath(), + newDbFile.toPath(), + java.nio.file.StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 迁移数据库失败: " + + e.getMessage()); + } + } + } + + + // 保存玩家进入区域时给予的效果 + private void savePlayerEffects(UUID uid, String playerName, + String areaName, List effects) { + try { + // 先删除该区域旧记录,避免重复 + PreparedStatement del = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + del.setString(1, uid.toString()); + del.setString(2, areaName); + del.executeUpdate(); + del.close(); + + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)"); + long now = System.currentTimeMillis(); + for (PotionEffectType type : effects) { + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, type.getName()); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, now); + ps.addBatch(); + } + ps.executeBatch(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 保存效果失败: " + e.getMessage()); + } + } + + // 查询玩家在某个区域获得的效果 + private List getPlayerEffects( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + PotionEffectType type = PotionEffectType + .getByName(rs.getString("effect_type")); + if (type != null) list.add(type); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + // 删除玩家在某个区域的效果记录 + private void removePlayerEffects(UUID uid, String areaName) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除效果失败: " + e.getMessage()); + } + } + + // 删除玩家所有效果记录 + private void removeAllPlayerEffects(UUID uid) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除全部效果失败: " + e.getMessage()); + } + } + + // 获取玩家所有记录的区域 + private List getPlayerAreas(UUID uid) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT DISTINCT area_name " + + "FROM player_effects WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("area_name")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询区域失败: " + e.getMessage()); + } + return list; + } + + // 查询所有待处理的效果(跨服重启也能恢复) + private List getAllPendingEffects() { + List list = new ArrayList<>(); + try { + Statement stmt = dbConnection.createStatement(); + ResultSet rs = stmt.executeQuery( + "SELECT uuid, player_name, area_name, " + + "effect_type FROM player_effects"); + while (rs.next()) { + list.add(new String[]{ + rs.getString("uuid"), + rs.getString("player_name"), + rs.getString("area_name"), + rs.getString("effect_type")}); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询全部效果失败: " + e.getMessage()); + } + return list; + } + + + private void writeDefaultConfig() { + File f = new File(rootDir, "末地保护区.txt"); + if (f.exists()) return; + + String endWorld = "world_the_end"; + for (org.bukkit.World w : Bukkit.getWorlds()) { + if (w.getEnvironment() + == org.bukkit.World.Environment.THE_END) { + endWorld = w.getName(); + break; + } + } + + final String ew = endWorld; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 末地保护区"); + pw.println("起点: " + ew + ",-30000000,-30000000"); + pw.println("终点: " + ew + ",30000000,30000000"); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("没收物品: BROWN_MUSHROOM,RED_MUSHROOM,MUSHROOM_STEW"); + pw.println("没收提示: 你携带了违禁品「蘑菇」,已被全部没收!"); + pw.println("通报批评: {player} 在末地违规携带蘑菇!"); + pw.println(); + pw.println("禁止放置方块"); + pw.println("禁止破坏方块"); + pw.println("禁止PVP"); + pw.println("禁止袭击"); + pw.println(); + pw.println("效果: 夜视 1 999"); + pw.println("进入提示: 末地保护区 - 请遵守规则"); + pw.println("离开提示: 已离开末地保护区"); + pw.close(); + plugin.getLogger().info("[防护] 生成末地配置, 世界: " + ew); + } catch (IOException ignored) { + } + } + + public void reload() { + loadAllAreas(); + loadWhitelists(); + // 强制输出醒目的中文日志 + plugin.getLogger().info("========================================"); + plugin.getLogger().info("[防护] 已重载 " + areas.size() + " 个区域"); + plugin.getLogger().info("[防护] 已重载白名单"); + plugin.getLogger().info("========================================"); + } + +// ==================== 消息格式化 ==================== + + /** + * 格式化区域消息 + * 1. & → §(颜色符号转换) + * 2.
和 \n → 换行 + * 3. 内容 → §l内容§r(加粗) + * 4. 自动添加 [区域防护] 前缀 + */ + private String formatAreaMsg(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + String msg = raw; + + // & → § + for (int i = 0; i <= 9; i++) { + msg = msg.replace("&" + i, "§" + i); + } + for (char c : "abcdefklnor".toCharArray()) { + msg = msg.replace("&" + c, "§" + c); + } + + // 换行 + msg = msg.replace("
", "\n"); + msg = msg.replace("\\n", "\n"); + + // HTML → Minecraft格式(栈式解析,支持嵌套) + msg = convertHtmlTags(msg); + + // 前缀 + msg = "§c§l【区域防护】§r " + msg; + + return msg; + } + + /** + * 栈式HTML标签解析 + * 处理 text rest 这样的嵌套标签 + * 内层关闭时重新应用外层格式 + */ + private String convertHtmlTags(String msg) { + StringBuilder sb = new StringBuilder(); + java.util.Deque stack = + new ArrayDeque<>(); + int i = 0; + + while (i < msg.length()) { + if (msg.charAt(i) == '<' + && i + 1 < msg.length()) { + if (msg.charAt(i + 1) == '/') { + // 关闭标签
+ int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 2, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0 && !stack.isEmpty() + && stack.peek() == code) { + stack.pop(); + if (!stack.isEmpty()) { + // 重置后重新应用外层格式 + sb.append("§r"); + for (char c : stack) { + sb.append("§").append(c); + } + } else { + sb.append("§r"); + } + } + i = end + 1; + } else { + // 开放标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 1, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0) { + stack.push(code); + sb.append("§").append(code); + } + i = end + 1; + } + } else { + sb.append(msg.charAt(i)); + i++; + } + } + return sb.toString(); + } + + private char htmlToMc(String tag) { + switch (tag) { + case "b": return 'l'; + case "i": return 'o'; + case "u": return 'n'; + case "s": return 'm'; + default: return 0; + } + } + +// =============== 加载 ==================== + + public void loadAllAreas() { + areas.clear(); + File[] files = rootDir.listFiles( + (File d, String n) -> n.endsWith(".txt")); + if (files == null) return; + for (File f : files) { + try { + String name = f.getName() + .replace(".txt", ""); + AreaConfig ac = parseArea(name, f); + areas.put(name, ac); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载失败: " + f.getName()); + } + } + } + + + private void initDatabase() { + try { + dbConnection = DriverManager.getConnection( + "jdbc:sqlite:" + newDbFile.getAbsolutePath()); + Statement stmt = dbConnection.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS player_effects (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "uuid TEXT NOT NULL," + + "player_name TEXT," + + "area_name TEXT NOT NULL," + + "effect_type TEXT NOT NULL," + + "effect_level INTEGER DEFAULT 0," + + "effect_duration INTEGER DEFAULT 999," + + "enter_time INTEGER NOT NULL)"); + stmt.executeUpdate( + "CREATE INDEX IF NOT EXISTS idx_uuid " + + "ON player_effects(uuid)"); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().severe( + "[防护] 数据库初始化失败: " + + e.getMessage()); + } + } + + +// ==================== 效果名称中英文映射 ==================== + + /** + * 将中文效果名转为 Bukkit 能识别的英文名 + */ + private String resolveEffectName(String name) { + if (name == null) return null; + String n = name.trim(); + // 如果已经是英文或命名空间格式,直接用 + if (n.contains(":") + || n.equals(n.toUpperCase())) { + return n; + } + // 中文 → 英文映射 + switch (n) { + case "伤害吸收": return "ABSORPTION"; + case "生命提升": return "HEALTH_BOOST"; + case "抗性提升": return "DAMAGE_RESISTANCE"; + case "抗火": return "FIRE_RESISTANCE"; + case "夜视": return "NIGHT_VISION"; + case "力量": return "INCREASE_DAMAGE"; + case "速度": return "SPEED"; + case "急迫": return "FAST_DIGGING"; + case "跳跃提升": return "JUMP"; + case "再生": return "REGENERATION"; + case "生命恢复": return "REGENERATION"; + case "治疗": return "HEAL"; + case "瞬间治疗": return "HEAL"; + case "伤害药水": return "HARM"; + case "瞬间伤害": return "HARM"; + case "虚弱": return "WEAKNESS"; + case "中毒": return "POISON"; + case "缓慢": return "SLOW"; + case "挖掘疲劳": return "SLOW_DIGGING"; + case "失明": return "BLINDNESS"; + case "隐身": return "INVISIBILITY"; + case "发光": return "GLOWING"; + // 村庄英雄 + case "村庄英雄": return "HERO_OF_THE_VILLAGE"; + case "幸运": return "LUCK"; + case "不幸": return "UNLUCK"; + case "水下呼吸": return "WATER_BREATHING"; + case "水下速掘": return "DOLPHINS_GRACE"; + case "缓降": return "LEVITATION"; + case "漂浮": return "LEVITATION"; + case "饱和": return "SATURATION"; + // 无效效果 + case "伤害": return "HARM"; + case "治疗药水": return "HEAL"; + default: + // 尝试直接用英文匹配 + return n; + } + } + + /** + * 全量容错:把全角字符统一转半角, + * 去掉零宽字符/不可见字符, + * 让用户怎么写都能正确解析。 + */ + private String normalizeLine(String input) { + if (input == null || input.isEmpty()) return ""; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + + // 跳过零宽字符和不可见字符 + if (c == '\u200B' // 零宽空格 + || c == '\u200C' // 零宽非连接 + || c == '\u200D' // 零宽连接 + || c == '\uFEFF' // BOM + || c == '\u00A0' // 不间断空格 + || c == '\u2028' // 行分隔符 + || c == '\u2029' // 段分隔符 + || c == '\u200E' // LTR标记 + || c == '\u200F' // RTL标记 + || c == '\u2060' // 字连接符 + || (c >= '\u2000' && c <= '\u200A')) { // 各种空格 + continue; + } + + // 全角→半角映射 + if (c >= '\uFF01' && c <= '\uFF5E') { + c = (char) (c - 0xFEE0); + } + + // 全角空格→半角空格 + if (c == '\u3000') { + c = ' '; + } + + sb.append(c); + } + + return sb.toString(); + } + + + private AreaConfig parseArea(String name, File f) + throws IOException { + AreaConfig ac = new AreaConfig(); + ac.name = name; + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean inBlockComment = false; + boolean inHtmlComment = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; +// ===== 全量容错预处理:全角→半角,去零宽字符 ===== + line = normalizeLine(line); + if (line.isEmpty()) continue; +// ===== 暴力调试:输出所有包含关键词的行 ===== + if (line.indexOf("游戏") >= 0 + || line.indexOf("模式") >= 0 + || line.indexOf("生存") >= 0 + || line.indexOf("创造") >= 0 + || line.indexOf("排除") >= 0 + || line.indexOf("豁免") >= 0 + || line.indexOf("强制") >= 0 + || line.indexOf("GameMode") >= 0 + || line.indexOf("SURVIVAL") >= 0 + || line.indexOf("CREATIVE") >= 0) { + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < line.length(); i++) { + hex.append(String.format("U+%04X ", (int) line.charAt(i))); + } + /* plugin.getLogger().info("[暴力调试] 行=[" + line + + "] 长度=" + line.length() + + " 字符码=" + hex.toString());*/ + } +// ===== 兜底扫描:确保和平白名单一定能解析 ===== + if (line.indexOf("白名单") >= 0 + || line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + int ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf('='); + if (ci < 0) ci = line.indexOf(' '); + + // 和平白名单 + if (line.indexOf("和平") >= 0 + && line.indexOf("白名单") >= 0 + && ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.peaceWhitelist.add(nm); + } + plugin.getLogger().info("[解析兜底] 和平白名单=" + + ac.peaceWhitelist); + } + + // 排除名单 + if (line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + if (ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.modeExempt.add(nm); + } + plugin.getLogger().info("[解析兜底] 排除名单=" + + ac.modeExempt); + } + } + } +// ===== 兜底结束 ===== + + if (inBlockComment) { + int endIdx = line.indexOf("*/"); + if (endIdx >= 0) { + inBlockComment = false; + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("/*")) { + int endIdx = line.indexOf("*/", 2); + if (endIdx >= 0) { + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + inBlockComment = true; + continue; + } + } + if (inHtmlComment) { + int endIdx = line.indexOf("-->"); + if (endIdx >= 0) { + inHtmlComment = false; + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("", 4); + if (endIdx >= 0) { + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + inHtmlComment = true; + continue; + } + } + if (line.startsWith("#") || line.startsWith("//")) { + continue; + } + int hashIdx = line.indexOf("#"); + if (hashIdx > 0) { + line = line.substring(0, hashIdx).trim(); + } + int slashIdx = line.indexOf("//"); + if (slashIdx > 0) { + line = line.substring(0, slashIdx).trim(); + } + if (line.isEmpty()) continue; + + if (line.startsWith("起点:")) { + String[] pp = line.substring("起点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x1 = Integer.parseInt(pp[1].trim()); + ac.z1 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("终点:")) { + String[] pp = line.substring("终点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x2 = Integer.parseInt(pp[1].trim()); + ac.z2 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("高度范围:")) { + String[] pp = line.substring("高度范围:".length()) + .trim().split("-"); + if (pp.length == 2) { + ac.yMin = Integer.parseInt(pp[0].trim()); + ac.yMax = Integer.parseInt(pp[1].trim()); + } + } else if (line.startsWith("没收物品:")) { + for (String s : line.substring("没收物品:".length()) + .trim().split("[,,]")) + ac.confiscateItems.add( + s.trim().toUpperCase()); + } else if (line.startsWith("禁止使用物品:")) { + for (String s : line.substring("禁止使用物品:".length()) + .trim().split("[,,]")) + ac.denyUseItems.add( + s.trim().toUpperCase()); + } else if (line.equals("禁止放置方块")) { + ac.denyBlockPlace = true; + } else if (line.equals("禁止破坏方块")) { + ac.denyBlockBreak = true; + } else if (line.equals("禁止PVP")) { + ac.denyPVP = true; + } else if (line.equals("禁止摔伤")) { + ac.denyFallDamage = true; + } else if (line.equals("禁止饥饿")) { + ac.denyHunger = true; + } else if (line.equals("禁止一切伤害")) { + ac.denyAllDamage = true; + } else if (line.equals("禁止丢弃物品")) { + ac.denyDrop = true; + } else if (line.equals("禁止末影珍珠")) { + ac.denyEnderPearl = true; + } else if (line.equals("禁止使用弓箭")) { + ac.denyBow = true; + } else if (line.equals("禁止骑乘")) { + ac.denyMount = true; + } else if (line.equals("禁止袭击")) { + ac.denyRaid = true; + } else if (line.equals("禁止药水")) { + ac.denyPotion = true; + } else if (line.equals("禁止爆炸")) { + ac.denyExplosion = true; + } else if (line.equals("禁止燃烧") + || line.equals("禁止火焰蔓延")) { + ac.denyFireSpread = true; + } else if (line.equals("清除所有效果") + || line.equals("清理所有效果")) { + ac.clearAllBadEffects = true; + } else if (line.startsWith("清除") + || line.startsWith("清理")) { + String effName = line.substring(2).trim(); + if (effName.endsWith("效果")) + effName = effName.substring(0, + effName.length() - 2); + if (!effName.isEmpty()) + ac.clearEffects.add(effName); + } else if (line.contains("和平模式") + && !line.contains("白名单") + && !line.contains("定时") + && !line.contains("开关")) { + ac.peaceMode = true; + plugin.getLogger().info("[解析] 和平模式已识别"); + + } else if (line.startsWith("效果")) { + String rest = line.substring(2).trim(); + // 去掉前导冒号、空格 + while (rest.length() > 0 + && (rest.charAt(0) == ':' + || rest.charAt(0) == ':' + || rest.charAt(0) == ' ' + || rest.charAt(0) == '\t')) { + rest = rest.substring(1); + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + // 第一个是效果名(可能是中文) + String effName = parts[0]; + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) {} + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) {} + } + + // ★ 用映射方法转为英文名 + String resolved = resolveEffectName(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → " + resolved + + " 等级=" + effLv + + " 时长=" + effDur); + + ac.giveEffects.add(new String[]{ + resolved, + String.valueOf(effLv), + String.valueOf(effDur)}); + // ★ 加日志确认解析结果 + PotionEffectType checkType = + resolveEffectType(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → type=" + + (checkType != null + ? checkType.getName() + : "NULL(失败)") + + " 等级=" + effLv + + " 时长=" + effDur); + + + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) { + } + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) { + } + } + ac.giveEffects.add(new String[]{ + parts[0], + String.valueOf(effLv), + String.valueOf(effDur)}); + } + } else if (line.startsWith("通报批评:")) { + ac.announceTemplate = line.substring( + "通报批评:".length()).trim(); + ac.enableAnnounce = true; + } else if (line.startsWith("没收提示:")) { + ac.confiscateMsg = line.substring( + "没收提示:".length()).trim(); + } else if (line.startsWith("进入提示:")) { + String raw = line.substring("进入提示:".length()).trim(); + ac.enterMsg = raw; + + // ★ 调试:打印原文和Unicode + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★进入提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("离开提示:")) { + String raw = line.substring("离开提示:".length()).trim(); + ac.leaveMsg = raw; + + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★离开提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("惩罚命令:")) { + ac.punishCommands.add(line.substring( + "惩罚命令:".length()).trim()); + } else if (line.startsWith("强制游戏模式:") + || line.startsWith("游戏模式:")) { + String prefix = line.startsWith("强制游戏模式:") + ? "强制游戏模式:" : "游戏模式:"; + String modeStr = line.substring(prefix.length()) + .trim().toUpperCase(); + // 容错:支持多种写法 + if (modeStr.equals("CREATIVE") || modeStr.equals("创造") + || modeStr.equals("1")) { + ac.enforceGameMode = "CREATIVE"; + } else if (modeStr.equals("SURVIVAL") || modeStr.equals("生存") + || modeStr.equals("0")) { + ac.enforceGameMode = "SURVIVAL"; + } else if (modeStr.equals("ADVENTURE") || modeStr.equals("冒险") + || modeStr.equals("2")) { + ac.enforceGameMode = "ADVENTURE"; + } else if (modeStr.equals("SPECTATOR") || modeStr.equals("旁观") + || modeStr.equals("spectator") + || modeStr.equals("3")) { + ac.enforceGameMode = "SPECTATOR"; + } + } else if (line.contains("排除名单") + || line.contains("模式排除") + || line.contains("模式豁免")) { + int colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf('='); + if (colonIdx >= 0) { + String rest = line.substring(colonIdx + 1).trim(); + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playerName = s.trim(); + if (!playerName.isEmpty()) { + ac.modeExempt.add(playerName); + } + } + } + + String rest = line.substring(/*idx*/ + 4).trim(); + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playername = s.trim(); + if (!name.isEmpty()) ac.modeExempt.add(name); + } + } + + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String wlName = s.trim(); + if (!wlName.isEmpty()) { + ac.peaceWhitelist.add(wlName); + } + } + } + + + + } else if (line.equals("禁止交互展示框") + || line.equals("禁止展示框")) { + ac.denyItemFrame = true; + + } + } + br.close(); + return ac; + } +// ===== 和平白名单操作 ===== + + // 添加生物名字到区域和平白名单 + public boolean addPeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.add(creatureName); + savePeaceWhitelist(ac); + return true; + } + + // 从区域和平白名单移除生物名字 + public boolean removePeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.remove(creatureName); + savePeaceWhitelist(ac); + return true; + } + +// ===== 模式排除名单操作 ===== + + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent e) { + Entity entity = e.getEntity(); + if (!isHostile(entity.getType().name())) return; + + Location loc = entity.getLocation(); + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + + if (ac == null || !ac.peaceMode) return; + + protectedEntities.put( + entity.getUniqueId(), + System.currentTimeMillis() + 5000); + + /* plugin.getLogger().info("[和平] " + + entity.getType().name() + + " 获得5秒保护期" + + " 区域=" + ac.name + + " 白名单=" + ac.peaceWhitelist + + " 白名单数量=" + ac.peaceWhitelist.size());*/ + } + + + public void cleanupProtectedEntities() { + long now = System.currentTimeMillis(); + int cleaned = 0; + Iterator> it = + protectedEntities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (now > entry.getValue()) { + it.remove(); + cleaned++; + } + } + if (cleaned > 0) { + /* plugin.getLogger().info("[和平模式] 清理过期保护: " + + cleaned + "个 剩余:" + + protectedEntities.size() + "个");*/ + } + } + + + public void banHostilesWithWhitelist(Player p, AreaConfig ac) { + long now = System.currentTimeMillis(); + // 看看附近到底有什么实体 + List nearby = p.getNearbyEntities(48, 48, 48); + /* plugin.getLogger().info("[和平] " + p.getName() + + " 附近实体总数=" + nearby.size());*/ + for (Entity ent : nearby) { + String typeName = ent.getType().name(); + /* plugin.getLogger().info("[和平] 实体: " + + typeName + " isHostile=" + isHostile(typeName));*/ + } + int scanned = 0; + int waiting = 0; + int saved = 0; + int banished = 0; + + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (!isHostile(ent.getType().name())) continue; + scanned++; + + UUID entUid = ent.getUniqueId(); + Long protectUntil = protectedEntities.get(entUid); + String customName = ent.getCustomName(); + + // 有保护期记录 + if (protectUntil != null) { + long remain = protectUntil - now; + + if (remain > 0) { + // 保护期内,精确剩余秒数 + int secondsLeft = (int) Math.ceil(remain / 1000.0); + waiting++; + + // 保护期内也检查名字(玩家可能刚贴上) + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + protectedEntities.remove(entUid); + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 白名单匹配,永久保留");*/ + continue; + } + + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 保护期剩余约" + secondsLeft + "秒");*/ + continue; + } + + // 保护期到了,最终检查 + protectedEntities.remove(entUid); + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + " 5秒到期");*/ + + // 到期时再查一次白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 到期但白名单匹配,保留");*/ + continue; + } + + // 没名字或不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名=[" + customName + "] 传送虚空");*/ + continue; + } + + // 没有保护期记录(非刷怪蛋生成的或记录被清了) + // 也检查一下白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 无保护期但白名单匹配,保留");*/ + continue; + } + + // 没有保护期且不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 无保护期,命名=[" + customName + "] 传送虚空");*/ + } + + /* plugin.getLogger().info("[和平] " + + " 扫描=" + scanned + + " 等待=" + waiting + + " 保留=" + saved + + " 传送=" + banished + + " 白名单=" + ac.peaceWhitelist);*/ + } + + private static final Set HOSTILE_TYPES = new HashSet<>(Arrays.asList( + "ZOMBIE", "ZOMBIE_VILLAGER", "HUSK", "DROWNED", + "SKELETON", "STRAY", "BOGGED", "WITHER_SKELETON", + "CREEPER", "SPIDER", "CAVE_SPIDER", "ENDERMITE", + "SILVERFISH", "BLAZE", "GHAST", "BIG_GHAST", + "MAGMA_CUBE", "SLIME", "WITCH", + "GUARDIAN", "ELDER_GUARDIAN", + "PILLAGER", "VINDICATOR", "EVOKER", "RAVAGER", + "ILLUSIONER", "VEX", + "PHANTOM", "SHULKER", "WARDEN", "BREEZE", + "PIGLIN", "PIGLIN_BRUTE", "ZOGLIN", + "ZOMBIFIED_PIGLIN", "ENDER_DRAGON" + )); + + private boolean isHostile(String typeName) { + return HOSTILE_TYPES.contains(typeName); + } + + // ==================== 白名单持久化 ==================== + + /** + * 保存白名单到文件 + * 路径:区域防护/whitelists/全局白名单.txt + * 路径:区域防护/whitelists/区域名.txt + */ + public void saveWhitelists() { + try { + // 1. 保存全局白名单 + PrintWriter pwGlobal = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(globalWhitelistFile), + StandardCharsets.UTF_8)); + for (String name : globalPlayerWhitelist) { + pwGlobal.println(name); + } + pwGlobal.close(); + + // 2. 删除旧的区域白名单文件(防止残留) + File[] oldFiles = whitelistDir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (oldFiles != null) { + for (File f : oldFiles) f.delete(); + } + + // 3. 保存各区域白名单 + for (Map.Entry> entry + : areaPlayerWhitelist.entrySet()) { + String name = entry.getKey(); + File areaFile = new File( + whitelistDir, name + ".txt"); + PrintWriter pwArea = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(areaFile), + StandardCharsets.UTF_8)); + for (String playerName : entry.getValue()) { + pwArea.println(playerName); + } + pwArea.close(); + } + + plugin.getLogger().info("[防护] 白名单已保存"); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 保存白名单失败: " + + e.getMessage()); + } + } + + + + // ==================== 区域检测 ==================== + + public AreaConfig getArea(String worldName, + int x, int y, int z) { + for (AreaConfig ac : areas.values()) { + if (ac.world.equalsIgnoreCase(worldName) + && inRect(x, z, ac) + && y >= ac.yMin + && y <= ac.yMax) { + return ac; + } + } + return null; + } + + private boolean inRect(int x, int z, AreaConfig ac) { + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + return x >= minX && x <= maxX + && z >= minZ && z <= maxZ; + } + + // ==================== 白名单检查 ==================== + + private boolean isPlayerWhitelisted(String player, + AreaConfig ac) { + if (globalPlayerWhitelist.contains( + player.toLowerCase())) + return true; + Set areaList = + areaPlayerWhitelist.get(ac.name); + return areaList != null + && areaList.contains(player.toLowerCase()); + } + + private boolean isItemBlacklisted(String itemId, + AreaConfig ac) { + if (globalItemBlacklist.contains( + itemId.toUpperCase())) + return true; + Set areaList = + areaItemBlacklist.get(ac.name); + return areaList != null + && areaList.contains(itemId.toUpperCase()); + } + + // ==================== 没收 ==================== + + private int confiscateItem(Player p, String itemTypeId) { + Material mat = Material.matchMaterial(itemTypeId); + if (mat == null) return 0; + ItemStack[] contents = p.getInventory().getContents(); + int removed = 0; + for (int i = 0; i < contents.length; i++) { + if (contents[i] != null + && contents[i].getType() == mat) { + removed += contents[i].getAmount(); + contents[i] = null; + } + } + p.getInventory().setContents(contents); + return removed; + } + + // ==================== 边框显示 ==================== + private void showBorder(Player p, AreaConfig ac, boolean temp) { + cancelBorder(p); + int taskId = Bukkit.getScheduler() + .runTaskTimer(plugin, new Runnable() { + @Override + public void run() { + if (!p.isOnline()) return; + Location loc = p.getLocation(); + int px = loc.getBlockX(); + int py = loc.getBlockY(); + int pz = loc.getBlockZ(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + for (int dy = 5; dy <= 30; dy += 5) { + for (int x = minX; x <= maxX; x += 4) { + for (int z = minZ; z <= maxZ; z += 4) { + if (x == minX || x == maxX + || z == minZ || z == maxZ) { + p.spawnParticle(Particle.END_ROD, + x + 0.5, py + dy, z + 0.5, + 1, 0, 0, 0, 0); + } + } + } + } + } + }, 0L, 20L).getTaskId(); + displayTaskIds.put(p.getUniqueId(), taskId); // taskId 是 int + if (temp) { + Bukkit.getScheduler().runTaskLater(plugin, + () -> cancelBorder(p), 300L); + } + } + + + private void cancelBorder(Player p) { + Integer taskId = displayTaskIds.remove(p.getUniqueId()); + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + } + + public void onPlayerQuit(String playerName) { + playerCurrentArea.remove(playerName); + } + + public void clearAllPlayerAreas() { + playerCurrentArea.clear(); + } + private void saveModeExempt(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("排除名单") + || trimmed.contains("模式排除") + || trimmed.contains("模式豁免")) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.modeExempt.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存模式排除名单失败: " + e.getMessage()); + } + } + + // ==================== 事件监听 ==================== + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + Location to = event.getTo(); + if (to == null) return; + + Location from = event.getFrom(); + if (from.getBlockX() == to.getBlockX() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + + // 防抖 + Long lastTime = lastMoveProcess.get(uid); + long now = System.currentTimeMillis(); + if (lastTime != null + && now - lastTime < MOVE_DEBOUNCE_MS) { + return; + } + lastMoveProcess.put(uid, now); + + // 检测区域 + String oldArea = playerCurrentArea.get(uid); + String newArea = null; + String forcedMode = null; + + for (Map.Entry entry + : areas.entrySet()) { + AreaConfig ac = entry.getValue(); + if (!ac.world.equals(to.getWorld().getName())) + continue; + int px = to.getBlockX(); + int pz = to.getBlockZ(); + int py = to.getBlockY(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + if (px >= minX && px <= maxX + && pz >= minZ && pz <= maxZ + && py >= ac.yMin && py <= ac.yMax) { + newArea = entry.getKey(); + forcedMode = ac.enforceGameMode; + break; + } + } + + /* plugin.getLogger().info( + "[防护-移动] " + p.getName() + + " 旧=[" + oldArea + + "] 新=[" + newArea + "]" + + " areas.size=" + areas.size() + + " world=" + to.getWorld().getName());*/ + + // ===== 2. 区域没变 ===== + if (Objects.equals(oldArea, newArea)) { + if (newArea != null) { + AreaConfig ac = areas.get(newArea); + if (ac != null) { + // ★ 每次移动都清除指定效果 + clearBadEffects(p, ac); + + // ★ 每次移动都强制游戏模式 + if (ac.enforceGameMode != null + && !ac.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + } + return; + } + + // 离开旧区域 + if (oldArea != null) { + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★开始离开处理: + + oldArea);*/ + + AreaConfig oldAc = areas.get(oldArea); + + // 离开提示 + if (oldAc != null && oldAc.leaveMsg != null + && !oldAc.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(oldAc.leaveMsg)); + } + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★收标: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + plugin.getLogger().info( + "[防护-移动-离开] 标记项: " + t.getName()); + } + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + plugin.getLogger().info( + "[防护-移动-离开] 取消旧延时"); + } + + // 创建延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = oldArea; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★创建延时任务: " + + toClear.size() + "个");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-移动-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); + + plugin.getLogger().info( + "[防护-移动-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size()); + + if (online == null || !online.isOnline()) { + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-移动-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-移动-清理] + cleared" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + } + } + + + // 进入新区域 + if (newArea != null) { + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + if (oldP != null) { + diffEffects(p, uid, oldP, newArea); + } + } + + AreaConfig newAc = areas.get(newArea); + if (newAc != null) { + if (newAc.enterMsg != null + && !newAc.enterMsg.isEmpty()) { + p.sendMessage( + formatAreaMsg(newAc.enterMsg)); + } + applyRegionEffects(p, uid, newArea, newAc); + // 清除效果 + clearBadEffects(p, newAc); + + } +// ===== 清除指定效果 ===== + if (newAc.clearEffects != null + && !newAc.clearEffects.isEmpty()) { + for (String effName : newAc.clearEffects) { + PotionEffectType type = + resolveEffectType(effName); + if (type != null && p.getPotionEffect(type) != null) { + p.removePotionEffect(type); + plugin.getLogger().info( + "[防护] ★清除效果: " + effName + + " 玩家=" + p.getName()); + } + } + } + +// ===== 清除所有效果 ===== + if (newAc.clearAllBadEffects) { + for (PotionEffect eff : p.getActivePotionEffects()) { + p.removePotionEffect(eff.getType()); + } + plugin.getLogger().info( + "[防护] ★清除所有效果, 玩家=" + p.getName()); + } + + if (forcedMode != null + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(forcedMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + if (newArea != null) { + playerCurrentArea.put(uid, newArea); + } else { + playerCurrentArea.remove(uid); + } + } +// ===== 清理袭击生物 ===== + + // 需要清理的袭击生物类型 + private static final Set RAID_MOBS = new HashSet<>(Arrays.asList( + "VEX", + "EVOKER", + "PILLAGER", + "VINDICATOR", + "WITCH", + "RAVAGER", + "PILLAGER" + )); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + Entity entity = event.getEntity(); + Location loc = entity.getLocation(); + + // 检查是否在防护区域内 + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + if (ac == null) return; + + // 禁止袭击:取消袭击事件 + if (ac.denyRaid) { + // 清理袭击生物 + String typeName = entity.getType().name(); + if (RAID_MOBS.contains(typeName)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] ★清理袭击生物: " + typeName + + " 区域=" + ac.name); + } + } + + // 没收物品检测 + if (ac.confiscateItems != null + && !ac.confiscateItems.isEmpty()) { + if (entity instanceof Item) { + Item item = (Item) entity; + String itemType = item.getItemStack() + .getType().name(); + if (ac.confiscateItems.contains(itemType)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] 没收违禁掉落物: " + + itemType); + } + } + } + } + + + /** + * 进入区域时发放效果 + * 流程:查DB → 有记录跳过写入 → 无记录写入DB + * → 给效果 → 贴标 + */ + private void applyRegionEffects(Player p, UUID uid, + String areaName, AreaConfig ac) { + /* plugin.getLogger().info( + "[防护-贴标] ★★★方法被调用! 玩家=" + + p.getName() + " 区域=" + areaName + + " ac=" + (ac != null) + + " 效果配置数=" + + (ac != null ? ac.giveEffects.size() : "null"));*/ + + if (ac == null || ac.giveEffects.isEmpty()) return; + if (ac.giveEffects.isEmpty()) return; + // 查DB:该玩家在此区域是否已有效果记录 + List dbEffects = + getPlayerEffectNames(uid, areaName); + + if (dbEffects.isEmpty()) { + // 无记录 → 写入DB + for (String[] eff : ac.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t == null) continue; + saveSingleEffect(uid, p.getName(), + areaName, t.getName()); + } + plugin.getLogger().info( + "[防护] DB写入, 玩家=" + p.getName() + + " 区域=" + areaName); + } else { + plugin.getLogger().info( + "[防护] DB已有记录, 跳过写入, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + + // 给效果 + 贴标 + List applied = new ArrayList<>(); + for (String[] eff : ac.giveEffects) { + try { + PotionEffectType type = + resolveEffectType(eff[0]); + if (type == null) continue; + int lv = Integer.parseInt(eff[1]) - 1; + int dur = Integer.parseInt(eff[2]) * 20; + + PotionEffect existing = + p.getPotionEffect(type); + if (existing != null + && existing.getAmplifier() >= lv) { + continue; + } + p.addPotionEffect( + new PotionEffect(type, dur, lv)); + applied.add(type); + } catch (Exception ignored) { + } + } + + // 贴标 + // 新代码(合并,不覆盖): + List existingMarks = + playerMarkedEffects.get(uid); + if (existingMarks != null && !existingMarks.isEmpty()) { + // 已有标记,合并新增的 + for (PotionEffectType t : applied) { + if (!existingMarks.contains(t)) { + existingMarks.add(t); + } + } + /* plugin.getLogger().info( + "[防护-贴标] 合并标记: " + + existingMarks.size() + + " 个, 玩家=" + p.getName());*/ + } else { + // 无标记,新建 + playerMarkedEffects.put(uid, applied); + /* plugin.getLogger().info( + "[防护-贴标] 新建标记: " + + applied.size() + + " 个, 玩家=" + p.getName());*/ + } + } + + /** + * 离开区域时,延时15秒清理效果 + * 流程:收标 → 15秒后清效果 → 确认清空 → 清DB + */ + private void scheduleEffectClear(Player p, UUID uid, + String areaName) { + // 收标 + List marked = + playerAppliedEffects.remove(uid); + + plugin.getLogger().info( + "[防护] 收标 " + (marked != null ? marked.size() : 0) + + " 个, 玩家=" + p.getName() + + " 区域=" + areaName); + + // 启动15秒延时 + pendingClearArea.put(uid, areaName); + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + + // 清除效果 + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + + // 确认清空后清DB + removePlayerEffects(uid, areaName); + plugin.getLogger().info( + "[防护] 15秒已到, 清除 " + + (marked != null ? marked.size() : 0) + + " 个效果 + DB, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + }.runTaskLater(plugin, 300L); // 15秒 = 300 tick + + pendingClearTask.put(uid, task); + } + public Set getAllAreaNames() { + return areas.keySet(); + } + + /** + * 15秒内进了新区域: + * 清除旧区域有、新区域没有的效果 + */ + private void diffEffects(Player p, UUID uid, + String oldAreaName, String newAreaName) { + AreaConfig oldAc = areas.get(oldAreaName); + AreaConfig newAc = areas.get(newAreaName); + if (oldAc == null || newAc == null) return; + + // ★ 从 pendingClearEffects 获取旧标记 + List oldMarked = + pendingClearEffects.remove(uid); + + if (oldMarked == null || oldMarked.isEmpty()) { + plugin.getLogger().info( + "[防护-跨区域] 无待清理效果"); + return; + } + + // 新区域的效果集合 + Set newTypes = new HashSet<>(); + if (newAc != null) { + for (String[] eff : newAc.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t != null) newTypes.add(t); + } + } + + int cleared = 0; + for (PotionEffectType type : oldMarked) { + if (!newTypes.contains(type)) { + p.removePotionEffect(type); + cleared++; + plugin.getLogger().info( + "[防护-跨区域] 清除: " + + type.getName()); + } else { + plugin.getLogger().info( + "[防护-跨区域] 保留: " + + type.getName() + + " (新区域也有)"); + } + } + + removePlayerEffects(uid, oldAreaName); + plugin.getLogger().info( + "[防护-跨区域] 完成: 清除" + + cleared + "/" + oldMarked.size() + + " 个, " + oldAreaName + + " → " + newAreaName); + } + +// ===== DB 辅助方法(简化版)===== + + /** + * 查询玩家在某个区域的效果记录(返回英文名列表) + */ + private List getPlayerEffectNames( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid=? AND area_name=?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("effect_type")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + /** + * 写入单条效果记录 + */ + private void saveSingleEffect(UUID uid, + String playerName, String areaName, + String effectType) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?,?,?,?,?,?,?)"); + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, effectType); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 写入效果失败: " + e.getMessage()); + } + } + + // ==================== 违禁品没收 ==================== + + /** + * 检查玩家是否豁免于没收 + * 全局白名单 / 区域白名单 / modeExempt / OP 都跳过 + */ + private boolean isExemptFromConfiscation( + Player p, String areaName) { + String name = p.getName(); + + if (globalPlayerWhitelist.contains(name)) + return true; + + Set areaWl = + areaPlayerWhitelist.get(areaName); + if (areaWl != null && areaWl.contains(name)) + return true; + + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) + return true; + + return false; + } + + /** + * 没收玩家背包中的违禁品 + */ + private void confiscateItems(Player p, + AreaConfig ac) { + boolean confiscated = false; + + for (int i = 0; + i < p.getInventory().getSize(); i++) { + ItemStack item = p.getInventory().getItem(i); + if (item == null) continue; + + String typeName = item.getType().name(); + if (ac.confiscateItems + .contains(typeName)) { + p.getInventory().setItem(i, null); + confiscated = true; + } + } + + if (confiscated) { + String msg = ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty() + ? ac.confiscateMsg + : "§c你的违禁品已被没收"; + p.sendMessage(msg); + + // 通报批评 + if (ac.enableAnnounce + && ac.announceTemplate != null) { + String text = ac.announceTemplate + .replace("{player}", + p.getName()); + Bukkit.broadcastMessage(text); + } + } + } + + + + private String getChineseGameMode(GameMode mode) { + switch (mode) { + case SURVIVAL: return "生存模式"; + case CREATIVE: return "创造模式"; + case ADVENTURE: return "冒险模式"; + case SPECTATOR: return "旁观模式"; + default: return mode.name(); + } + } + + private void handleEnter(Player p, AreaConfig ac) { + UUID uid = p.getUniqueId(); + + // 清除负面效果 + clearBadEffects(p, ac); + + // 给予效果 + List given = new ArrayList<>(); + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + given.add(type); + } + } + + removePlayerEffects(uid, ac.name); + if (!given.isEmpty()) { + // savePlayerEffects(uid, p.getName(), ac.name, given); + } + + // 白名单检查(最先执行) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 游戏模式切换(只对非白名单玩家) + if (ac.enforceGameMode != null) { + boolean exempt = isPlayerExemptFromModeChange(p, ac); + if (!exempt) { + GameMode target = GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + p.sendMessage("§e§l[区域防护] §f游戏模式已切换为: " + + getChineseGameMode(target)); + } + } + } + + // 和平模式/袭击检查 + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + handleConfiscate(p, ac); + } + + private boolean isPlayerExemptFromModeChange( + Player p, AreaConfig ac) { + // if (p.isOp()) return true; + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return true; + } + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return true; + } + if (ac.modeExempt.contains(p.getName())) { + return true; + } + return false; + } + + private void banRangedHostiles(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (isRangedHostile(ent.getType().name())) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + /** + * 按配置清除效果 + * denyRaid → 清除袭击相关 + * denyAllEffects → 清除所有负面+中性 + * denyAllEffects > denyRaid(全清优先) + */ + private void clearBadEffects(Player p, AreaConfig ac) { + // 和平模式 > 清除所有效果 > 禁止单个 + if (ac.peaceMode || ac.clearAllBadEffects) { + PotionEffectType[] allBad = { + PotionEffectType.SLOWNESS, + PotionEffectType.MINING_FATIGUE, + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.NAUSEA, + PotionEffectType.BLINDNESS, + PotionEffectType.HUNGER, + PotionEffectType.WEAKNESS, + PotionEffectType.POISON, + PotionEffectType.WITHER, + PotionEffectType.LEVITATION, + PotionEffectType.UNLUCK, + PotionEffectType.DARKNESS, + PotionEffectType.WIND_CHARGED, + PotionEffectType.WEAVING, + PotionEffectType.OOZING, + PotionEffectType.INFESTED, + PotionEffectType.BAD_OMEN, + PotionEffectType.TRIAL_OMEN, + PotionEffectType.RAID_OMEN + }; + for (PotionEffectType type : allBad) { + p.removePotionEffect(type); + } + return; + } + + // 禁止袭击 → 自动关联清除 + if (ac.denyRaid) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + p.removePotionEffect(PotionEffectType.RAID_OMEN); + } + + // 配置文件单个清除 + for (String effName : ac.clearEffects) { + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.removePotionEffect(type); + } + } + } + + + private PotionEffectType resolveEffectType(String name) { + if (name == null || name.isEmpty()) return null; + + // 去掉"效果"后缀 + String clean = name; + if (clean.endsWith("效果")) { + clean = clean.substring(0, clean.length() - 2); + } + + // 硬编码中文名→英文ID映射(不依赖任何外部Map) + switch (clean) { + // 负面 + case "缓慢": + return resolveEffectType("slowness"); + case "挖掘疲劳": + return resolveEffectType("mining_fatigue"); + case "瞬间伤害": + return resolveEffectType("instant_damage"); + case "反胃": + return resolveEffectType("nausea"); + case "失明": + return resolveEffectType("blindness"); + case "饥饿": + return resolveEffectType("hunger"); + case "虚弱": + return resolveEffectType("weakness"); + case "中毒": + return resolveEffectType("poison"); + case "凋零": + return resolveEffectType("wither"); + case "飘浮": + return resolveEffectType("levitation"); + case "霉运": + return resolveEffectType("unluck"); + case "黑暗": + return resolveEffectType("darkness"); + case "蓄风": + return resolveEffectType("wind_charged"); + case "盘丝": + return resolveEffectType("weaving"); + case "渗浆": + return resolveEffectType("oozing"); + case "寄生": + return resolveEffectType("infested"); + // 中性 + case "不祥之兆": + case "不祥征兆": + return resolveEffectType("bad_omen"); + case "袭击之兆": + case "袭击征兆": + return resolveEffectType("raid_omen"); + case "试炼之兆": + case "试炼征兆": + return resolveEffectType("trial_omen"); + // 正面 + case "迅捷": + return resolveEffectType("speed"); + case "急迫": + return resolveEffectType("haste"); + case "力量": + return resolveEffectType("strength"); + case "瞬间治疗": + return resolveEffectType("instant_health"); + case "跳跃提升": + return resolveEffectType("jump_boost"); + case "生命恢复": + return resolveEffectType("regeneration"); + case "抗性提升": + return resolveEffectType("resistance"); + case "抗火": + return resolveEffectType("fire_resistance"); + case "水下呼吸": + return resolveEffectType("water_breathing"); + case "隐身": + return resolveEffectType("invisibility"); + case "夜视": + return resolveEffectType("night_vision"); + case "发光": + return resolveEffectType("glowing"); + case "生命提升": + return resolveEffectType("health_boost"); + case "伤害吸收": + return resolveEffectType("absorption"); + case "饱和": + return resolveEffectType("saturation"); + case "幸运": + return resolveEffectType("luck"); + case "村庄英雄": + return resolveEffectType("hero_of_the_village"); + case "缓降": + return resolveEffectType("slow_falling"); + case "潮涌能量": + return resolveEffectType("conduit_power"); + case "海豚的恩惠": + return resolveEffectType("dolphins_grace"); + case "致命中毒": + return resolveEffectType("poison"); + default: + break; + } + + +// 兜底:直接用原版英文ID查(★ 注意是 PotionEffectType.getByName,不是 resolveEffectType) + PotionEffectType fallback = PotionEffectType.getByName(clean); + if (fallback != null) return fallback; + fallback = PotionEffectType.getByName(clean.toLowerCase()); + if (fallback != null) return fallback; + + plugin.getLogger().warning("[防护] 无法识别效果: " + name); + return null; + + } + + + private static final Map EFFECT_MAP + = new HashMap<>(); + + + private void clearRaidEffects(Player p) { + boolean cleared = false; + try { + if (p.hasPotionEffect(PotionEffectType.BAD_OMEN)) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + try { + if (p.hasPotionEffect(PotionEffectType.RAID_OMEN)) { + p.removePotionEffect(PotionEffectType.RAID_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + if (cleared) { + p.sendMessage("§c§l[区域防护] §f已清除袭击相关效果"); + } + } + + public Map> + getPendingClearEffects() { + return pendingClearEffects; + } + + public void onPlayerOffline(UUID uid, String playerName, + String areaName) { + // 清除该玩家在该区域的所有效果 + List given = getPlayerEffects(uid, areaName); + for (PotionEffectType type : given) { + // 只清除我们给予的效果 + Player p = Bukkit.getPlayer(uid); + if (p != null) { + p.removePotionEffect(type); + } + } + // 清除DB记录 + removePlayerEffects(uid, areaName); + plugin.getLogger().info("[防护] " + playerName + + " 下线,已清除" + given.size() + + "个效果并清理DB"); + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent e) { + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + if (from.getWorld() == to.getWorld() + && from.distance(to) < 5) return; + + Player p = e.getPlayer(); + UUID uid = p.getUniqueId(); + + // 标记传送中 + teleporting.add(uid); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + teleporting.remove(uid); + }, 5L); + + AreaConfig fromArea = getArea( + from.getWorld().getName(), + from.getBlockX(), from.getBlockY(), + from.getBlockZ()); + AreaConfig toArea = getArea( + to.getWorld().getName(), + to.getBlockX(), to.getBlockY(), + to.getBlockZ()); + + String fromName = fromArea != null + ? fromArea.name : null; + String toName = toArea != null + ? toArea.name : null; + + /* plugin.getLogger().info( + "[防护-传送] ★★★进入onTeleport: " + + p.getName() + + " [" + fromName + "] → [" + toName + "]" + + " teleporting=" + teleporting.contains(uid));*/ + + if (Objects.equals(fromName, toName)) { + /* plugin.getLogger().info( + "[防护-传送] 区域相同, 跳过");*/ + return; + } + + // ===== 离开旧区域 ===== + if (fromName != null) { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★开始离开处理: " + + fromName);*/ + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★收标结果: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + /* plugin.getLogger().info( + "[防护-传送-离开] 标记项: " + + t.getName());*/ + } + } else { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★标记为null! " + + "尝试其他key查找...");*/ + // 遍历查找可能的标记 + for (Map.Entry> entry + : playerMarkedEffects.entrySet()) { + if (entry.getKey().equals(uid)) { + /* plugin.getLogger().info( + "[防护-传送-离开] 找到标记! 效果数=" + + entry.getValue().size());*/ + } + } + /* plugin.getLogger().info( + "[防护-传送-离开] playerMarkedEffects大小=" + + playerMarkedEffects.size());*/ + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 创建15秒延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = fromName; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★创建延时任务: " + + toClear.size() + "个效果");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-传送-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); +/* + plugin.getLogger().info( + "[防护-传送-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size() + + " 区域=" + clearArea);*/ + + if (online == null + || !online.isOnline()) { + plugin.getLogger().info( + "[防护-传送-清理] 玩家不在线, 清DB"); + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-传送-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-传送-清理] ★★★完成: 清除" + + cleared + "/" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + + } + + // 离开提示 + if (fromArea != null && fromArea.leaveMsg != null + && !fromArea.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(fromArea.leaveMsg)); + } + } + + // ===== 进入新区域 ===== + if (toName != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] ★★★开始进入处理: " + + toName);*/ + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + plugin.getLogger().info( + "[防护-传送-进入] 取消旧延时, 旧区域=" + + oldP); + if (oldP != null) { + diffEffects(p, uid, oldP, toName); + } + } + + // 进入提示 + if (toArea != null && toArea.enterMsg != null + && !toArea.enterMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(toArea.enterMsg)); + } + + // 发放效果 + if (toArea != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] 调用applyRegionEffects");*/ + applyRegionEffects(p, uid, toName, toArea); +// 清除效果 + clearBadEffects(p, toArea); + + if (toArea.confiscateItems != null + && !toArea.confiscateItems.isEmpty() + && !isExemptFromConfiscation( + p, toName)) { + confiscateItems(p, toArea); + } + } + + // 强制模式 + if (toArea != null && toArea.enforceGameMode != null + && !toArea.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, toName)) { + GameMode target = GameMode.valueOf( + toArea.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + // 更新记录 + if (toName != null) { + playerCurrentArea.put(uid, toName); + } else { + playerCurrentArea.remove(uid); + } + } + + + public Map getPendingClearTasks() { + return pendingClearTask; + } + + public Map> + getPlayerMarkedEffects() { + return playerMarkedEffects; + } + + public void handleInside(Player p, AreaConfig ac) { + clearBadEffects(p, ac); + + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + // 全白或区白 → 跳过所有限制性规则(包括没收) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + handleConfiscate(p, ac); + } + + public void cleanupExpiredProtections() { + protectedEntities.entrySet().removeIf( + entry -> System.currentTimeMillis() > entry.getValue()); + } + + // 禁止袭击:只驱逐袭击怪物 + private void banRaidMobs(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(32, 32, 32)) { + String t = ent.getType().name(); + if (t.equals("PILLAGER") + || t.equals("VINDICATOR") + || t.equals("EVOKER") + || t.equals("RAVAGER") + || t.equals("VEX") + || t.equals("WITCH") + || t.equals("ILLUSIONER") + || t.equals("ZOMBIE_VILLAGER")) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(voidLoc.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + public Set getAreaNames() { + return areas.keySet(); + } + + public Collection getPendingTasks() { + return pendingClearTask.values(); + } + + + // 判断是否为远程攻击敌对生物 + private boolean isRangedHostile(String typeName) { + switch (typeName) { + // ===== 骷髅系 ===== + case "SKELETON": // 骷髅(弓箭) + case "STRAY": // 流浪者(迟缓之箭) + case "BOGGED": // 虫蚀骷髅(毒箭) + case "WITHER_SKELETON": // 凋灵骷髅(近战但算灾厄) + + // ===== 灾厄村民 ===== + case "PILLAGER": // 掠夺者(弩) + case "ILLUSIONER": // 幻术师(弓箭+失明) + case "EVOKER": // 幻魔者(唤魔) + case "VINDICATOR": // 卫道士(斧头近战) + case "VEX": // 恼鬼(剑近战,但会穿墙飞行) + + // ===== 法术/投射 ===== + case "BLAZE": // 烈焰人(火球) + case "WITCH": // 女巫(药水) + + // ===== 飞行/爆炸 ===== + case "GHAST": // 恶魂(火球) + case "PHANTOM": // 幻翼(俯冲) + case "BIG_GHAST": // 大恶魂(1.21新增) + + // ===== 水下 ===== + case "GUARDIAN": // 守卫者(激光) + case "ELDER_GUARDIAN": // 远古守卫者(激光) + + // ===== 远程特殊 ===== + case "SHULKER": // 潜影贝(潜影弹) + case "BREEZE": // 旋风人(风弹) + + // ===== 蜘蛛系(远程毒液攻击) ===== + case "SPIDER": // 蜘蛛(白天远程吐丝) + case "CAVE_SPIDER": // 洞穴蜘蛛(毒) + + // ===== 1.21新增 ===== + case "WIND_CHARGED": // 蓄风怪(风弹) + case "WEAVING": // 盘丝怪(吐丝) + case "OOZING": // 渗浆怪(史莱姆) + case "INFESTED": // 寄生怪(蠹虫) + + // ===== 盔甲/变种(部分版本) ===== + case "ARMORED_SKELETON": // 盔甲骷髅 + case "ARCHER_SKELETON": // 弓箭骷髅(部分版本) + case "SNOW_GOLEM": // 雪傀儡(不是敌对但远程) + + // ===== 亡灵骑乘 ===== + case "HUSK": // 尸壳(近战但属于灾厄系) + case "DROWNED": // 溺尸(三叉戟远程) + case "ZOMBIE_VILLAGER": // 僵尸村民 + case "ZOMBIE_PIGMAN": // 僵尸猪灵 + case "ZOMBIFIED_PIGLIN": // 僵尸猪灵(1.16+) + case "PIGLIN_BRUTE": // 猪灵蛮兵 + + // ===== 猪灵(弓箭) ===== + case "PIGLIN": // 猪灵(金弩远程) + + // ===== 末影系 ===== + case "ENDER_DRAGON": // 末影龙(远程吐息) + case "WARDEN": // 监守者(远程音波) + + // ===== 其他 ===== + case "CREPER": // 苦力怕(爆炸不算远程但危险) + case "CREEPER": // 苦力怕 + + return true; + default: + return false; + } + } + + + + private void applyEffects(Player p, AreaConfig ac) { + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + + PotionEffectType type = resolveEffectType(effName); + plugin.getLogger().info("[防护调试] 给予: " + + effName + " -> " + type); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + plugin.getLogger().info("[防护调试] 已给予: " + + effName + " 等级" + level + + " 持续" + duration + "秒"); + } + } + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockPlace + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止放置方块"); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockBreak + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏方块"); + } + } + + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + // 保护性规则:对所有人无条件生效 + if (ac.denyAllDamage) { e.setCancelled(true); return; } + if (ac.denyFallDamage + && e.getCause() == EntityDamageEvent.DamageCause.FALL) + e.setCancelled(true); + } + + + @EventHandler + public void onPVP(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyPVP + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + + @EventHandler + public void onFood(FoodLevelChangeEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyHunger) + e.setCancelled(true); + } + + + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyDrop + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止丢弃物品"); + } + } + + // 替换整个 onInteract 方法 + @EventHandler + public void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand == null) return; + + // ===== 选区工具 ===== + if (hand.getType() == Material.BREEZE_ROD) { + ItemMeta meta = hand.getItemMeta(); + if (meta != null && meta.getDisplayName() != null + && meta.getDisplayName().contains("区域选择")) { + Action action = e.getAction(); + boolean isLeft = (action == Action.LEFT_CLICK_AIR + || action == Action.LEFT_CLICK_BLOCK); + boolean isRight = (action == Action.RIGHT_CLICK_AIR + || action == Action.RIGHT_CLICK_BLOCK); + if (isLeft || isRight) { + e.setCancelled(true); + Block block = e.getClickedBlock(); + if (block == null) { + try { block = p.getTargetBlockExact(5); } + catch (Exception ignored) {} + } + if (block == null) return; + UUID uid = p.getUniqueId(); + if (isLeft) { + pos1.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置1: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } else { + pos2.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置2: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } + } + return; + } + } + + // ===== 放置实体类物品时不检查(展示框、矿车、船、地图等)===== + Material mat = hand.getType(); + if (mat == Material.ITEM_FRAME + || mat == Material.GLOW_ITEM_FRAME + || mat == Material.MINECART + || mat == Material.TNT_MINECART + || mat == Material.CHEST_MINECART + || mat == Material.HOPPER_MINECART + || mat == Material.OAK_BOAT + || mat == Material.SPRUCE_BOAT + || mat == Material.BIRCH_BOAT + || mat == Material.JUNGLE_BOAT + || mat == Material.ACACIA_BOAT + || mat == Material.DARK_OAK_BOAT + || mat == Material.MANGROVE_BOAT + || mat == Material.CHERRY_BOAT + || mat == Material.BAMBOO_RAFT + || mat == Material.MAP + || mat == Material.ARMOR_STAND) { + return; + } + + // ===== 区域规则检查 ===== + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + + // 末影珍珠禁止 + if (ac.denyEnderPearl + && hand.getType() == Material.ENDER_PEARL) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用末影珍珠"); + return; + } + + // 弓箭禁止 + if (ac.denyBow + && (hand.getType() == Material.BOW + || hand.getType() == Material.CROSSBOW)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用弓箭"); + return; + } + + // 白名单跳过 + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 禁止使用物品 + String typeId = hand.getType().name(); + if (ac.denyUseItems.contains(typeId)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用此物品"); + return; + } + + // 没收检查 + handleConfiscate(p, ac); + } + + + + @EventHandler + public void onExplosion(EntityExplodeEvent e) { + Iterator it = e.blockList().iterator(); + while (it.hasNext()) { + Block b = it.next(); + AreaConfig ac = getArea( + b.getWorld().getName(), + b.getX(), b.getY(), b.getZ()); + if (ac != null && ac.denyExplosion) + it.remove(); + } + } + public void loadWhitelists() { + globalPlayerWhitelist.clear(); + areaPlayerWhitelist.clear(); + + if (!whitelistDir.exists()) { + whitelistDir.mkdirs(); + return; + } + + try { + // 加载全局白名单 + if (globalWhitelistFile.exists()) { + List lines = + java.nio.file.Files.readAllLines( + globalWhitelistFile.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + globalPlayerWhitelist.add(name); + } + } + } + + // 加载各区域白名单 + File[] files = whitelistDir.listFiles( + (File d, String n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (files != null) { + for (File f : files) { + String areaName = f.getName() + .replace(".txt", ""); + Set set = + ConcurrentHashMap.newKeySet(); + List lines = + java.nio.file.Files.readAllLines( + f.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + set.add(name); + } + } + if (!set.isEmpty()) { + areaPlayerWhitelist.put( + areaName, set); + } + } + } + + plugin.getLogger().info( + "[防护] 白名单加载完成,全局: " + + globalPlayerWhitelist.size() + + ",区域数: " + + areaPlayerWhitelist.size()); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载白名单失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onPotionSplash(PotionSplashEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + AreaConfig ac = getArea( + shooter.getWorld().getName(), + shooter.getLocation().getBlockX(), + shooter.getLocation().getBlockY(), + shooter.getLocation().getBlockZ()); + if (ac != null && ac.denyPotion + && !isPlayerWhitelisted(shooter.getName(), ac)) { + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止使用药水"); + } + } + + /** + * 解析区域名:先精准匹配,再模糊匹配 + * @param input 用户输入的区域名 + * @return 匹配到的区域名,无匹配返回null + */ + public String resolveAreaName(String input) { + if (input == null || input.isEmpty()) return null; + if (input.equalsIgnoreCase("global")) return "global"; + + // 第一步:精准匹配 + for (String name : areas.keySet()) { + if (name.equalsIgnoreCase(input)) { + return name; + } + } + + // 第二步:包含匹配(用户只打了部分名字) + for (String name : areas.keySet()) { + if (name.contains(input) || input.contains(name)) { + return name; + } + } + + // 第三步:编辑距离模糊匹配(著称→主城) + String bestMatch = null; + int bestDistance = Integer.MAX_VALUE; + for (String name : areas.keySet()) { + int dist = levenshteinDistance( + input.toLowerCase(), name.toLowerCase()); + if (dist < bestDistance) { + bestDistance = dist; + bestMatch = name; + } + } + + // 容错阈值:编辑距离 <= 名字长度的一半 + if (bestMatch != null) { + int threshold = Math.max(1, bestMatch.length() / 2); + if (bestDistance <= threshold) { + return bestMatch; + } + } + + return null; + } + + /** + * 判断字符串是否是区域名(精准或模糊) + */ + private boolean isAreaName(String name) { + if (name.equalsIgnoreCase("global")) return false; + return resolveAreaName(name) != null; + } + + /** + * 获取模糊匹配的实际区域名(用于提示用户) + */ + private String getResolvedName(String input) { + return resolveAreaName(input); + } + + /** + * 编辑距离算法(莱文斯坦距离) + */ + private int levenshteinDistance(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + for (int i = 0; i <= s1.length(); i++) dp[i][0] = i; + for (int j = 0; j <= s2.length(); j++) dp[0][j] = j; + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], + Math.min(dp[i - 1][j], dp[i][j - 1])); + } + } + } + return dp[s1.length()][s2.length()]; + } + + /** + * 智能重排参数:找出子命令位置,重排为标准顺序 + * 标准顺序:/protect 子命令 参数1 参数2 ... + */ + private String[] smartReorderArgs(String[] original) { + if (original.length <= 1) return original; + + Set knownSubs = new HashSet<>(Arrays.asList( + "add", "remove", "additem", "removeitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite", + "list", "listitem", "创建", "删除", "重载", + "工具", "expand", "contraction", + "on", "off", "tempon", "modeexempt" + )); + + String first = original[0].toLowerCase(); + if (knownSubs.contains(first)) { + return original; + } + + int subIndex = -1; + for (int i = 0; i < original.length; i++) { + if (knownSubs.contains(original[i].toLowerCase())) { + subIndex = i; + break; + } + } + + if (subIndex < 0) { + return original; + } + + String[] reordered = new String[original.length]; + reordered[0] = original[subIndex]; + int idx = 1; + for (int i = 0; i < original.length; i++) { + if (i == subIndex) continue; + reordered[idx] = original[i]; + idx++; + } + + return reordered; + } + + // ==================== 管理命令 ==================== + public boolean handleCommand(CommandSender sender, + String[] args) { + args = smartReorderArgs(args); + if (args.length == 0) { + showHelp(sender); + return true; + } + String sub = args[0].toLowerCase(); +// ===== 诊断命令 ===== + if (sub.equals("sdf1debug")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + sender.sendMessage("§e===== 调试信息 ====="); + sender.sendMessage("§7当前区域: " + + playerCurrentArea.get(uid)); + sender.sendMessage("§7标记效果数: " + + (playerMarkedEffects.containsKey(uid) + ? playerMarkedEffects.get(uid).size() + : "无标记")); + sender.sendMessage("§7待清理任务: " + + pendingClearTask.containsKey(uid)); + sender.sendMessage("§7区域数: " + areas.size()); + + if (playerMarkedEffects.containsKey(uid)) { + List marked = + playerMarkedEffects.get(uid); + for (PotionEffectType type : marked) { + sender.sendMessage("§7 标记: " + + type.getName() + + " 身上有=" + + (p.getPotionEffect(type) != null)); + } + } + return true; + } + +// ===== 强制清理命令(测试用)===== + if (sub.equals("testclear")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + List marked = + playerMarkedEffects.remove(uid); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + sender.sendMessage("§a已强制清理 " + + marked.size() + " 个效果"); + } else { + sender.sendMessage("§c无标记效果可清理"); + } + + // 取消延时任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + sender.sendMessage("§a已取消延时任务"); + } + + removePlayerEffects(uid, + playerCurrentArea.getOrDefault(uid, "")); + sender.sendMessage("§aDB已清理"); + return true; + } + + // ===== 工具 ===== + if (sub.equals("工具") || sub.equals("wand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + ItemStack tool = new ItemStack(WAND); + ItemMeta meta = tool.getItemMeta(); + meta.setDisplayName("§a§l区域选择工具"); + meta.setLore(Arrays.asList( + "§7左键: 位置1", "§7右键: 位置2")); + tool.setItemMeta(meta); + p.getInventory().addItem(tool); + p.sendMessage("§a§l[防护] §f已获取选择工具"); + return true; + } + + // ===== 创建 ===== + if (sub.equals("创建") || sub.equals("create")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 创建 <名>"); + return true; + } + Player p = (Player) sender; + UUID u = p.getUniqueId(); + if (!pos1.containsKey(u) + || !pos2.containsKey(u)) { + p.sendMessage("§c请先用工具选点"); + return true; + } + String areaName = args[1]; + File f = new File(rootDir, areaName + ".txt"); + if (f.exists()) { + p.sendMessage("§c区域已存在"); + return true; + } + Location l1 = pos1.get(u); + Location l2 = pos2.get(u); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 区域: " + areaName); + pw.println("起点: " + l1.getWorld().getName() + + "," + l1.getBlockX() + + "," + l1.getBlockZ()); + pw.println("终点: " + l2.getWorld().getName() + + "," + l2.getBlockX() + + "," + l2.getBlockZ()); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("# 禁止放置方块"); + pw.println("# 禁止破坏方块"); + pw.println("# 禁止PVP"); + pw.println("# 效果: 夜视 1 999"); + pw.println("# 进入提示: 欢迎来到保护区"); + pw.println("# 离开提示: 已离开保护区"); + pw.close(); + p.sendMessage("§a§l[防护] §f区域 " + + areaName + " 已创建"); + loadAllAreas(); + } catch (IOException ex) { + p.sendMessage("§c创建失败"); + } + return true; + } + + // ===== 和平白名单 ===== + if (sub.equals("addname")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别: " + + args[1] + " 或 " + args[2]); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 和平白名单"); + return true; + } + sender.sendMessage("§c用法: /protect addname " + + "<区域> <名字>"); + return true; + } + if (sub.equals("removename")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + parsed[0]); + return true; + } + sender.sendMessage("§c用法: /protect removename " + + "<区域> <名字>"); + return true; + } + if (sub.equals("listname")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listname <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 和平白名单:"); + if (ac.peaceWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.peaceWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 模式排除 ===== + if (sub.equals("addwhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect addwhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("removewhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect removewhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("listwhite")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listwhite <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 模式排除名单:"); + if (ac.modeExempt.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.modeExempt) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 列表 ===== + if (sub.equals("列表") || sub.equals("list")) { + if (args.length == 2) { + if (args[1].equalsIgnoreCase("global")) { + sender.sendMessage("§a全局白名单:"); + if (globalPlayerWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n + : globalPlayerWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + String an = args[1]; + Set wl = + areaPlayerWhitelist.get(an); + sender.sendMessage("§a" + an + " 白名单:"); + if (wl == null || wl.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : wl) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + if (areas.isEmpty()) { + sender.sendMessage("§7暂无区域"); + } else { + sender.sendMessage("§e§l区域列表:"); + for (Map.Entry en + : areas.entrySet()) { + sender.sendMessage("§a " + en.getKey() + + " §7规则:" + + en.getValue().ruleCount() + + "条"); + } + } + return true; + } + + // ===== 重载 ===== + if (sub.equals("重载") || sub.equals("reload")) { + reload(); + sender.sendMessage("§a已重载 " + + areas.size() + " 个区域"); + return true; + } + + // ===== 物品黑名单 ===== + if (sub.equals("additem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + args[1]); + return true; + } + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + itemName); + return true; + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + } else { + areaItemBlacklist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()) + .add(itemName); + sender.sendMessage("§a已加黑: " + + itemName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect additem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("removeitem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (!globalItemBlacklist + .contains(itemName)) { + sender.sendMessage( + "§c全局黑名单中没有: " + + itemName); + return true; + } + globalItemBlacklist.remove(itemName); + sender.sendMessage("§a已移除: " + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.remove(itemName); + } else { + Set list = + areaItemBlacklist.get(areaName); + if (list != null) { + list.remove(itemName); + } + } + sender.sendMessage("§a已移除: " + + itemName); + return true; + } + sender.sendMessage("§c用法: /protect removeitem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("listitem")) { + if (args.length < 2) { + sender.sendMessage( + "§a全局物品黑名单:"); + if (globalItemBlacklist.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item + : globalItemBlacklist) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + Set list = + areaItemBlacklist.get(args[1]); + sender.sendMessage("§a" + args[1] + + " 物品黑名单:"); + if (list == null || list.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item : list) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + + // ===== expand 扩建 ===== + if (sub.equals("expand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ -= amount; + else if (dir.equals("南")) maxZ += amount; + else if (dir.equals("东")) maxX += amount; + else if (dir.equals("西")) minX -= amount; + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f扩建 §e" + + amount + "§f格"); + return true; + } + + // ===== contract 收缩 ===== + if (sub.equals("contraction") + || sub.equals("contract")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ += amount; + else if (dir.equals("南")) maxZ -= amount; + else if (dir.equals("东")) maxX -= amount; + else if (dir.equals("西")) minX += amount; + if (minX > maxX || minZ > maxZ) { + p.sendMessage("§c收缩过度"); + return true; + } + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f收缩 §e" + + amount + "§f格"); + return true; + } + + // ===== 删除 ===== + if (sub.equals("删除") || sub.equals("delete")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 删除 <名>"); + return true; + } + File f = new File(rootDir, args[1] + ".txt"); + if (f.exists()) { + f.delete(); + areas.remove(args[1]); + sender.sendMessage("§a已删除: " + args[1]); + } else { + sender.sendMessage("§c不存在: " + args[1]); + } + return true; + } + + // ===== add 加白名单 ===== + if (sub.equals("add")) { + if (args.length == 2) { + String name = args[1]; + if (globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 已在全局白名单中"); + return true; + } + globalPlayerWhitelist.add(name); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + + playerName); + } else { + Set wl = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()); + wl.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已加白: " + + playerName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect add " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== remove 移除白名单 ===== + if (sub.equals("remove")) { + if (args.length == 2) { + String name = args[1]; + if (!globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 不在全局白名单中"); + return true; + } + globalPlayerWhitelist.remove(name); + saveWhitelists(); + sender.sendMessage("§a已从全局白名单移除: " + + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.remove(playerName); + saveWhitelists(); + sender.sendMessage("§a已移除: " + + playerName); + } else { + Set wl = + areaPlayerWhitelist.get(areaName); + if (wl != null) { + wl.remove(playerName); + } + saveWhitelists(); + sender.sendMessage("§a已从 " + areaName + + " 移除: " + playerName); + } + return true; + } + sender.sendMessage("§c用法: /protect remove " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== on/off/tempon 边框 ===== + if (sub.equals("on")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, false); + p.sendMessage("§a§l[防护] §f已显示 " + + ac.name + " 边框"); + return true; + } + if (sub.equals("off")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + cancelBorder((Player) sender); + ((Player) sender).sendMessage( + "§a§l[防护] §f已关闭边框显示"); + return true; + } + if (sub.equals("tempon")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, true); + p.sendMessage("§a§l[防护] §f临时显示 " + + ac.name + " 边框(15秒)"); + return true; + } + + showHelp(sender); + return true; + } + + + private String[] parseAreaAndTarget(String arg1, String arg2) { + String r1 = resolveAreaName(arg1); + if (r1 != null) { + return new String[]{r1, arg2}; + } + String r2 = resolveAreaName(arg2); + if (r2 != null) { + return new String[]{r2, arg1}; + } + return null; + } + + private boolean playerExists(String playerName) { + // 在线玩家 + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.getName().equalsIgnoreCase(playerName)) return true; + } + // 离线玩家 + @SuppressWarnings("deprecation") + OfflinePlayer op = Bukkit.getOfflinePlayer(playerName); + return op.hasPlayedBefore(); + } + + + private boolean validateArea(CommandSender sender, String areaName) { + if (!areas.containsKey(areaName)) { + sender.sendMessage("§c区域不存在: " + areaName); + return false; + } + return true; + } + + + private String matname(Material mat) { + String name = mat.name(); + String[] parts = name.split("_"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (sb.length() > 0) sb.append(" "); + sb.append(part.substring(0, 1).toUpperCase()) + .append(part.substring(1).toLowerCase()); + } + return sb.toString(); + } + private String yawToDir(float yaw) { + // MC中: 0=南, 90=西, 180=北, -90/270=东 + yaw = yaw % 360; + if (yaw < 0) yaw += 360; + if (yaw >= 315 || yaw < 45) return "南"; + if (yaw >= 45 && yaw < 135) return "西"; + if (yaw >= 135 && yaw < 225) return "北"; + return "东"; + } + private void saveAreaConfig(AreaConfig ac) { + File f = new File(rootDir, ac.name + ".txt"); + if (!f.exists()) return; + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + if (line.trim().startsWith("起点:")) { + lines.add("起点: " + ac.world + + "," + ac.x1 + "," + ac.z1); + } else if (line.trim().startsWith("终点:")) { + lines.add("终点: " + ac.world + + "," + ac.x2 + "," + ac.z2); + } else { + lines.add(line); + } + } + br.close(); + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + for (String l : lines) pw.println(l); + pw.close(); + } catch (IOException ignored) {} + } + + private void showHelp(CommandSender s) { + s.sendMessage("§e§l==== 区域防护 ===="); + s.sendMessage("§a/protect 工具 §7选地工具"); + s.sendMessage("§a/protect 创建 <名> §7创建区域"); + s.sendMessage("§a/protect 列表 §7区域列表"); + s.sendMessage("§a/protect 重载 §7重载配置"); + s.sendMessage("§a/protect 删除 <名> §7删除区域"); + s.sendMessage("§a/protect add [区域] <玩家> §7玩家加白"); + s.sendMessage("§a/protect remove [区域] <玩家> §7玩家删白"); + s.sendMessage("§a/protect additem [区域] <物品> §7物品加黑"); + s.sendMessage("§a/protect removeitem [区域] <物品> §7物品删黑"); + s.sendMessage("§a/protect on §7显示边框"); + s.sendMessage("§a/protect off §7关闭边框"); + s.sendMessage("§a/protect tempon §7临时边框(15秒)"); + s.sendMessage("§a/protect list §7列出全局白名单"); + s.sendMessage("§a/protect list <区域> §7列出区域白名单"); + s.sendMessage("§a/protect listitem §7列出全局物品黑名单"); + s.sendMessage("§a/protect listitem <区域> §7列出区域物品黑名单"); + + } +// ==================== 白名单管理公共方法 ==================== + + // ==================== 白名单操作 ==================== + + public boolean isPlayerGlobalWhitelisted( + String playerName) { + return globalPlayerWhitelist.contains(playerName); + } + + public boolean addPlayerToGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean isPlayerAreaWhitelisted( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + return wl != null && wl.contains(playerName); + } + + public boolean addPlayerToAreaWhitelist( + String areaName, String playerName) { + Set set = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap.newKeySet()); + boolean ok = set.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromAreaWhitelist( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + boolean ok = wl != null && wl.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + + + public int handleConfiscate(Player p, AreaConfig ac) { + if (isPlayerWhitelisted(p.getName(), ac)) return 0; + Set allItems = new HashSet<>(ac.confiscateItems); + allItems.addAll(globalItemBlacklist); + Set areaList = areaItemBlacklist.get(ac.name); + if (areaList != null) allItems.addAll(areaList); + + if (allItems.isEmpty()) return 0; + + int totalRemoved = 0; + String confiscatedNames = ""; + + for (String itemId : allItems) { + // 过滤掉区域名和无效ID + Material mat = Material.matchMaterial(itemId); + if (mat == null) { + plugin.getLogger().warning( + "[防护] 跳过无效物品: " + itemId); + continue; + } + ItemStack[] all = p.getInventory().getContents(); + int count = 0; + for (int i = 0; i < all.length; i++) { + if (all[i] != null && all[i].getType() == mat) { + count += all[i].getAmount(); + all[i] = null; + } + } + p.getInventory().setContents(all); + + ItemStack off = p.getInventory().getItemInOffHand(); + if (off != null && off.getType() == mat) { + count += off.getAmount(); + p.getInventory().setItemInOffHand(null); + } + + if (count > 0) { + totalRemoved += count; + if (!confiscatedNames.isEmpty()) + confiscatedNames += "§7、§e"; + confiscatedNames += "§e" + matname(mat) + + " x" + count; + } + } + + if (totalRemoved > 0) { + String msg = (ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty()) + ? ac.confiscateMsg + : "你携带了违禁品,已被没收"; + p.sendMessage("§c§l[区域防护] §f" + msg); + p.sendMessage("§7没收物品: " + confiscatedNames); + + if (ac.enableAnnounce + && ac.announceTemplate != null + && !ac.announceTemplate.isEmpty()) { + String ann = ac.announceTemplate + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)) + .replace("{items}", confiscatedNames); + Bukkit.broadcastMessage("§c§l[区域防护] §f" + ann); + } + for (String cmd : ac.punishCommands) { + String finalCmd = cmd + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)); + Bukkit.getScheduler().runTask(plugin, + () -> Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), finalCmd)); + } + } + return totalRemoved; + } + public void clearBlacklists() { + globalItemBlacklist.clear(); + areaItemBlacklist.clear(); + plugin.getLogger().info("[防护] 已清空所有黑名单"); + } + + @EventHandler + public void onEntityDamage( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑 ===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + // 展示框被点击(旋转/放入/取出) + @EventHandler + public void onItemFrameInteract( + PlayerInteractAtEntityEvent e) { + Entity clicked = e.getRightClicked(); + String typeName = clicked.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + Player p = e.getPlayer(); + + // 全局白放行 + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(clicked); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + // 非白名单 → 拦截 + // 记录当前旋转角度 + final Rotation savedRotation; + final Entity frame = clicked; + if (clicked instanceof org.bukkit.entity.ItemFrame) { + savedRotation = + ((org.bukkit.entity.ItemFrame) clicked) + .getRotation(); + } else { + savedRotation = null; + } + + e.setCancelled(true); + + // 下一tick强制恢复旋转 + if (savedRotation != null) { + final Rotation restore = savedRotation; + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (frame.isValid() + && frame instanceof org.bukkit.entity.ItemFrame) { + ((org.bukkit.entity.ItemFrame) frame) + .setRotation(restore); + } + }, 1L); + } + + p.sendMessage("§c§l[区域防护] §f禁止交互展示框"); + + // 驱逐远程敌对生物 + banRangedHostiles(p, ac); + } + + + + // 展示框保护:检查展示框本身+周围方块所属区域 + + // ===== 展示框攻击拦截(左键/投射物破坏)===== + @EventHandler + public void onEntityDamageByEntity( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + // 展示框被攻击 + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑(原有的,不动)===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + private AreaConfig findFrameArea(Entity frame) { + Location loc = frame.getLocation(); + int bx = loc.getBlockX(); + int by = loc.getBlockY(); + int bz = loc.getBlockZ(); + String world = loc.getWorld().getName(); + + AreaConfig ac = getArea(world, bx, by, bz); + if (ac != null && ac.denyItemFrame) return ac; + + int[][] offsets = { + {0, 0, 1}, {0, 0, -1}, + {1, 0, 0}, {-1, 0, 0}, + {0, 1, 0}, {0, -1, 0} + }; + for (int[] off : offsets) { + ac = getArea(world, + bx + off[0], by + off[1], bz + off[2]); + if (ac != null && ac.denyItemFrame) return ac; + } + return null; + } + + // ===== 投射物击中展示框(弓箭/弩)===== + @EventHandler + public void onProjectileHit( + org.bukkit.event.entity.ProjectileHitEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + Entity hit = e.getHitEntity(); + if (hit == null) return; + String typeName = hit.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(shooter.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(hit); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(shooter.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + } + + + // ===== 区域外火焰不能蔓延进区域 ===== + @EventHandler + public void onBlockSpread(BlockSpreadEvent e) { + Block block = e.getBlock(); + AreaConfig tgtArea = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (tgtArea != null && tgtArea.denyFireSpread) { + e.setCancelled(true); + } + } + + // ===== 区域内禁止点燃方块 ===== + @EventHandler + public void onIgnite(BlockIgniteEvent e) { + Player p = e.getPlayer(); + if (p == null) return; // 非玩家点燃的(闪电等) + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f此区域禁止点燃物品"); + } + } + + // ===== 区域内方块着火(TNT、岩浆等)也阻止 ===== + @EventHandler + public void onBlockBurn(BlockBurnEvent e) { + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) + e.setCancelled(true); + } + + + private void savePeaceWhitelist(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("和平") + && trimmed.contains("白名单")) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.peaceWhitelist.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存和平白名单失败: " + e.getMessage()); + } + } + + + @EventHandler + public void onPaintingPlace( + org.bukkit.event.player.PlayerBucketEmptyEvent e) { + // 暂不处理 + } + + + // ==================== 数据类 ==================== + + public static class AreaConfig { + public String name = ""; + public String world = ""; + public int x1, z1, x2, z2; + public int yMin = 0, yMax = 255; + public List confiscateItems = new ArrayList<>(); + public List denyUseItems = new ArrayList<>(); + public List clearEffects = new ArrayList<>(); // 清除单个效果名 + public boolean clearAllBadEffects = false; // 清除所有负面+中性 + public List giveEffects = new ArrayList<>(); // [效果名, 等级, 秒数] + public List punishCommands = new ArrayList<>(); + public boolean denyBlockPlace = false; + public boolean denyBlockBreak = false; + public boolean denyPVP = false; + public boolean denyFallDamage = false; + public boolean denyHunger = false; + public boolean denyAllDamage = false; + public boolean denyMount = false; + public boolean denyDrop = false; + public boolean denyEnderPearl = false; + public boolean denyBow = false; + public boolean denyPotion = false; + public boolean denyExplosion = false; + public boolean enableAnnounce = false; + public String announceTemplate = ""; + public String confiscateMsg = ""; + public String enterMsg = ""; + public String leaveMsg = ""; + public boolean denyRaid = false; + public boolean denyFireSpread = false; + public boolean denyAllEffects = false;; + public boolean peaceMode = false; + public boolean denyItemFrame = false; + public Set peaceWhitelist = new HashSet<>(); + public String enforceGameMode = null; // 强制游戏模式 + public Set modeExempt = new HashSet<>(); // 模式排除名单 + + + + + private static final Map EFFECT_MAP + = new HashMap<>(); + static { + // 负面效果 + EFFECT_MAP.put("缓慢", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("凋零", PotionEffectType.WITHER); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("霉运", PotionEffectType.UNLUCK); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("蓄风", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生", PotionEffectType.INFESTED); + // 中性效果 + EFFECT_MAP.put("不祥之兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("不祥征兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("袭击征兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("试炼征兆", PotionEffectType.TRIAL_OMEN); + // 正面效果 + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); + EFFECT_MAP.put("瞬间治疗", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("跳跃提升", PotionEffectType.JUMP_BOOST); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性提升", PotionEffectType.RESISTANCE); + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("生命提升", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("村庄英雄", PotionEffectType.HERO_OF_THE_VILLAGE); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); + EFFECT_MAP.put("潮涌能量", PotionEffectType.CONDUIT_POWER); + EFFECT_MAP.put("海豚的恩惠", PotionEffectType.DOLPHINS_GRACE); + + + // 带效果二字 + // 补充带"效果"后缀的别名 + EFFECT_MAP.put("黑暗效果", PotionEffectType.DARKNESS); + EFFECT_MAP.put("不祥之兆效果", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆效果", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆效果", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("缓慢效果", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("中毒效果", PotionEffectType.POISON); + EFFECT_MAP.put("凋零效果", PotionEffectType.WITHER); + EFFECT_MAP.put("失明效果", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿效果", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱效果", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("反胃效果", PotionEffectType.NAUSEA); + EFFECT_MAP.put("飘浮效果", PotionEffectType.LEVITATION); + EFFECT_MAP.put("发光效果", PotionEffectType.GLOWING); + EFFECT_MAP.put("霉运效果", PotionEffectType.UNLUCK); + EFFECT_MAP.put("蓄风效果", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝效果", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆效果", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生效果", PotionEffectType.INFESTED); + EFFECT_MAP.put("挖掘疲劳效果", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害效果", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("村庄英雄效果", PotionEffectType.HERO_OF_THE_VILLAGE); + } + + + public int ruleCount() { + int c = 0; + if (!confiscateItems.isEmpty()) c++; + if (denyBlockPlace) c++; + if (denyBlockBreak) c++; + if (denyPVP) c++; + if (denyFallDamage) c++; + if (denyHunger) c++; + if (denyAllDamage) c++; + if (denyDrop) c++; + if (denyMount) c++; + if (denyEnderPearl) c++; + if (denyBow) c++; + if (denyRaid) c++; + if (denyPotion) c++; + if (denyExplosion) c++; + if (denyFireSpread) c++; + if (denyAllEffects) c++; + if (!clearEffects.isEmpty()) c++; + if (clearAllBadEffects) c++; + if (!giveEffects.isEmpty()) c++; + if (enableAnnounce) c++; + if (!punishCommands.isEmpty()) c++; + if (!denyUseItems.isEmpty()) c++; + if (peaceMode) c++; + if (denyItemFrame) c++; + if (!peaceWhitelist.isEmpty()) c++; + + return c; + } + } + } \ No newline at end of file diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/BondManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/BondManager.java new file mode 100644 index 0000000..ea78152 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/BondManager.java @@ -0,0 +1,697 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.io.File; +import java.sql.*; +import java.util.*; + +public class BondManager { + + private final Main plugin; + private Connection db; + + // ===== 交易类型常量 ===== + public static final String TX_ADMIN_GIVE = "admin_give"; + public static final String TX_ADMIN_DEDUCT = "admin_deduct"; + public static final String TX_TRANSFER_OUT = "transfer_out"; + public static final String TX_TRANSFER_IN = "transfer_in"; + public static final String TX_REDEEM = "redeem"; + public static final String TX_SHOP_BUY = "shop_buy"; + public static final String TX_DAILY_SIGN = "daily_sign"; + public static final String TX_FREEZE = "freeze"; + public static final String TX_UNFREEZE = "unfreeze"; + + + public static final int MAX_WATER_DAYS = 14; + public static final int ADMIN_MAX_WATER_DAYS = 30; + + public BondManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + // ======================== 数据库初始化 ======================== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 债券余额(含冻结状态) + st.execute("CREATE TABLE IF NOT EXISTS bonds (" + + "player_name TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "status TEXT DEFAULT 'normal')"); + + // 兼容旧表 + try { + st.execute("ALTER TABLE bonds " + + "ADD COLUMN status " + + "TEXT DEFAULT 'normal'"); + } catch (SQLException ignored) {} + + // 旧流水(保留兼容) + st.execute("CREATE TABLE IF NOT EXISTS bond_log (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "reason TEXT DEFAULT ''," + + "time INTEGER DEFAULT 0)"); + + // 新交易流水表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "bond_transaction (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "type TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "target_player TEXT DEFAULT ''," + + "operator TEXT DEFAULT ''," + + "reason TEXT DEFAULT ''," + + "balance_after INTEGER DEFAULT 0," + + "time INTEGER DEFAULT 0)"); + + try { + st.execute("CREATE INDEX " + + "IF NOT EXISTS idx_tx_pt " + + "ON bond_transaction(" + + "player_name, time)"); + } catch (SQLException ignored) {} + try { + st.execute("ALTER TABLE bond_transaction " + + "ADD COLUMN balance_before " + + "INTEGER DEFAULT 0"); + } catch (SQLException ignored) {} + + + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[Bond] DB init failed: " + + e.getMessage(), e); + } + } + + // ======================== 账户状态 ======================== + + public boolean isFrozen(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return "frozen".equals(s); + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return false; + } + + public String getAccountStatus(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return s != null ? s : "normal"; + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return "normal"; + } + + public void freezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='frozen' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + public void unfreezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='normal' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + private void ensureAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR IGNORE INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,0,'normal')"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + // ======================== 余额查询 ======================== + + public int getBonds(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + int v = 0; + if (rs.next()) v = rs.getInt("amount"); + rs.close(); ps.close(); + return v; + } catch (SQLException e) { return 0; } + } + + /** 该玩家是否在 bond 表中有真实记录 */ + public boolean hasBondAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM bonds " + + "WHERE player_name=? " + + "AND amount > 0"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + boolean exists = rs.next(); + rs.close(); ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ======================== 增加债券 ======================== + + public int addBonds(String player, int amount) { + return addBondsInternal(player, amount, + TX_ADMIN_GIVE, "", "", + "管理员发放"); + } + + public int addBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return -1; + return addBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + private int addBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + int after = before + amount; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status)" + + " VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, amount); + return after; + } + + // ======================== 扣除债券 ======================== + + + + private boolean deductBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + if (before < amount) return false; + int after = before - amount; + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET amount=? " + + "WHERE player_name=?"); + ps.setInt(1, after); + ps.setString(2, player); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, -amount); + return true; + } + + // ======================== 流水写入 ======================== + + private void logTransaction(String player, + String type, + int amount, + String targetPlayer, + String operator, + String reason, + int balanceBefore, + int balanceAfter) { + try { + // 合并模式:shop_buy 30秒内同商品合并 + if ("shop_buy".equals(type) + && reason != null + && !reason.isEmpty()) { + long mergeWindow = + System.currentTimeMillis() - 30000L; + PreparedStatement chk = + db.prepareStatement( + "SELECT id, amount " + + "FROM bond_transaction " + + "WHERE player_name=? " + + "AND type=? " + + "AND reason=? " + + "AND time > ? " + + "ORDER BY time DESC " + + "LIMIT 1"); + chk.setString(1, player); + chk.setString(2, type); + chk.setString(3, reason); + chk.setLong(4, mergeWindow); + ResultSet rs = chk.executeQuery(); + if (rs.next()) { + int existId = rs.getInt("id"); + int existAmt = rs.getInt("amount"); + rs.close(); + chk.close(); + PreparedStatement upd = + db.prepareStatement( + "UPDATE bond_transaction " + + "SET amount=?, " + + "balance_after=?, " + + "time=? " + + "WHERE id=?"); + upd.setInt(1, existAmt + amount); + upd.setInt(2, balanceAfter); + upd.setLong(3, + System.currentTimeMillis()); + upd.setInt(4, existId); + upd.executeUpdate(); + upd.close(); + return; + } + rs.close(); + chk.close(); + } + + // 原始插入 + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_transaction " + + "(player_name, type, amount," + + " target_player, operator," + + " reason, balance_before," + + " balance_after, time)" + + " VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, type); + ps.setInt(3, amount); + ps.setString(4, + targetPlayer != null ? targetPlayer : ""); + ps.setString(5, + operator != null ? operator : ""); + ps.setString(6, + reason != null ? reason : ""); + ps.setInt(7, balanceBefore); + ps.setInt(8, balanceAfter); + ps.setLong(9, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + // ======================== 扣除债券 ======================== + + /** 完整版:含冻结检查 + 流水记录 */ + public boolean deductBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return false; + return deductBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + public boolean checkoutCart(String player, + String[] itemNames, + int[] subtotals) { + int total = 0; + for (int s : subtotals) total += s; + if (isFrozen(player)) return false; + + int before = getBonds(player); + if (before < total) return false; + int after = before - total; + + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?,'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + + int running = before; + for (int i = 0; i < itemNames.length; i++) { + int next = running - subtotals[i]; + logTransaction(player, "shop_buy", subtotals[i], + "", "商店系统", + "购物车购买-" + itemNames[i], + running, next); + running = next; + } + + logLegacy(player, -total); + return true; + } + + public boolean setBonds(String player, int amount) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setInt(3, amount); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return true; + } + + // ======================== 转账 ======================== + + public TransferResult transfer(String from, + String to, + int amount) { + if (from.equalsIgnoreCase(to)) + return new TransferResult(false, + "不能给自己转账"); + if (isFrozen(from)) + return new TransferResult(false, + "你的账户已被冻结"); + if (isFrozen(to)) + return new TransferResult(false, + to + " 的账户已被冻结"); + if (isSameIP(from, to)) + return new TransferResult(false, + "不能与同IP玩家转账"); + + if (!deductBondsInternal(from, amount, + TX_TRANSFER_OUT, to, from, + "转账给" + to)) + return new TransferResult(false, "债券不足"); + + addBondsInternal(to, amount, + TX_TRANSFER_IN, from, from, + "收到来自" + from + "的转账"); + + return new TransferResult(true, "转账成功"); + } + + // ======================== 同IP检测 ======================== + + public boolean isSameIP(String p1, String p2) { + String ip1 = getPlayerIPAny(p1); + String ip2 = getPlayerIPAny(p2); + if (ip1 == null || ip2 == null) return false; + return ip1.equals(ip2); + } + + /** 获取同IP下的所有其他在线玩家 */ + public List getSameIPPlayers(String player) { + String ip = getPlayerIPAny(player); + if (ip == null) return Collections.emptyList(); + List list = new ArrayList<>(); + for (Player p : + plugin.getServer().getOnlinePlayers()) { + String pIp = plugin.getPlayerIP(p); + if (pIp != null && pIp.equals(ip) + && !p.getName() + .equalsIgnoreCase(player)) { + list.add(p.getName()); + } + } + return list; + } + + private String getPlayerIPAny(String player) { + // 在线:直接取 + Player p = plugin.getServer() + .getPlayerExact(player); + if (p != null) { + String ip = plugin.getPlayerIP(p); + if (ip != null) return ip; + } + // 离线:查主库 + try { + Object v = plugin.getDb() + .getField(player, "last_login_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + try { + Object v = plugin.getDb() + .getField(player, "register_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + return null; + } + + // ======================== 流水查询 ======================== + + public List> getTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } +// 放在 getTransactions 方法后面: + + /** + * 获取该玩家全部流水(不限时间,供打印使用) + */ + public List> + getAllTransactions(String player) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "ORDER BY time ASC"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("player_name", + rs.getString("player_name")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } +// 放在 getAllTransactions 方法后面: + + /** + * 获取所有有流水记录的玩家名(去重) + */ + public List getAllPlayerNames() { + List list = new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT DISTINCT player_name " + + "FROM bond_transaction " + + "ORDER BY player_name ASC"); + while (rs.next()) { + list.add(rs.getString("player_name")); + } + rs.close(); st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTransferTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "AND (type=? OR type=?) " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ps.setString(3, TX_TRANSFER_OUT); + ps.setString(4, TX_TRANSFER_IN); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } + + + private void logLegacy(String player, int amount) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_log " + + "(player_name,amount,time) " + + "VALUES (?,?,?)"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + public Map getLog( + String player, int limit) { + Map info = new LinkedHashMap<>(); + info.put("balance", getBonds(player)); + return info; + } + + // ======================== 转账结果 ======================== + + public static class TransferResult { + public final boolean success; + public final String message; + public TransferResult(boolean success, + String message) { + this.success = success; + this.message = message; + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/BondPrinter.java b/backup/20260605_152529/src/main/java/Sdf1_login/BondPrinter.java new file mode 100644 index 0000000..674ec3c --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/BondPrinter.java @@ -0,0 +1,378 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; + +public class BondPrinter { + + private final Main plugin; + private final File outputDir; + + private static final SimpleDateFormat FILE_SDF = + new SimpleDateFormat( + "yyyy-MM-dd_HH-mm-ss"); + private static final SimpleDateFormat CELL_SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + private static final LinkedHashMap + TYPE_NAMES = new LinkedHashMap<>(); + + static { + TYPE_NAMES.put("admin_give", "管理员发放"); + TYPE_NAMES.put("admin_deduct", "管理员扣除"); + TYPE_NAMES.put("transfer_out", "转出"); + TYPE_NAMES.put("transfer_in", "转入"); + TYPE_NAMES.put("redeem", "CDK兑换"); + TYPE_NAMES.put("shop_buy", "商城购买"); + TYPE_NAMES.put("shop_sell", "商城出售"); // ← 补上 + TYPE_NAMES.put("daily_sign", "签到奖励"); + TYPE_NAMES.put("freeze", "冻结"); + TYPE_NAMES.put("unfreeze", "解冻"); + + } + + public BondPrinter(Main plugin) { + this.plugin = plugin; + this.outputDir = new File( + plugin.getDataFolder(), "printer"); + if (!this.outputDir.exists()) { + this.outputDir.mkdirs(); + } + } + + // ============ 打印单个玩家 ============ + + public void printPlayer(String player, + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintPlayer( + player, sender)); + } + + private void doPrintPlayer(String player, + CommandSender sender) { + try { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + + String fileName = player + "_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + deleteOld(player); + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, + escXml(player)); + writeXmlRow(pw, + new String[]{ + "时间", "类型", + "金额", "对象", + "操作者", "理由", + "操作前余额", + "操作后余额"}); + + for (Map tx : txs) { + writeXmlRow(pw, new String[]{ + txTime(tx), + txTypeName(tx), + String.valueOf( + txInt(tx, + "amount")), + txStr(tx, + "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")) + }); + } + + writeXmlFooter(pw); + pw.flush(); + } finally { + pw.close(); + } + + if (sender != null) { + sender.sendMessage("§a[打印] §f" + + player + " §a流水已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + txs.size() + + " §7条,§724小时后删除"); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 打印全服 ============ + + public void printAll(CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintAll(sender)); + } + + private void doPrintAll(CommandSender sender) { + try { + List players = + plugin.getBonds() + .getAllPlayerNames(); + if (players.isEmpty()) { + if (sender != null) { + sender.sendMessage("§c没有流水数据"); + } + return; + } + + String fileName = "全服流水_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + // ===== 收集所有流水 ===== + // key = 时间戳+玩家+金额,用于去重 + // value = 一行数据 + List allRows = new ArrayList<>(); + Set seenKeys = new HashSet<>(); + + for (String player : players) { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + for (Map tx : txs) { + int txId = ((Number) tx.get("id")) + .intValue(); + String dedupKey = + String.valueOf(txId); + if (!seenKeys.add(dedupKey)) { + continue; + } + + String timeStr = txTime(tx); + long timeMs = 0; + Object t = tx.get("time"); + if (t != null) { + timeMs = + ((Number) t).longValue(); + } + + allRows.add(new String[]{ + player, + timeStr, + txTypeName(tx), + String.valueOf( + txInt(tx, "amount")), + txStr(tx, "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")), + // 额外存时间戳用于排序 + String.valueOf(timeMs) + }); + } + } + + // ===== 按时间戳排序 ===== + allRows.sort((a, b) -> { + long ta = Long.parseLong( + a[a.length - 1]); + long tb = Long.parseLong( + b[b.length - 1]); + return Long.compare(ta, tb); + }); + + // ===== 写入文件 ===== + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, "全服流水"); + writeXmlRow(pw, new String[]{ + "玩家", "时间", "类型", + "金额", "对象", "操作者", + "理由", "操作前余额", + "操作后余额" + }); + + for (String[] row : allRows) { + // 去掉最后的时间戳列 + String[] cells = + new String[row.length - 1]; + System.arraycopy(row, 0, + cells, 0, cells.length); + writeXmlRow(pw, cells); + } + + writeXmlFooter(pw); + pw.flush(); + + if (sender != null) { + sender.sendMessage( + "§a[打印] §f全服流水 §a已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + players.size() + + " 名玩家,§f" + + allRows.size() + " §7条"); + sender.sendMessage( + "§7按时间从早到晚排序"); + sender.sendMessage( + "§724小时后自动删除"); + } + } finally { + pw.close(); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 清理 ============ + + public void cleanupOldFiles() { + long now = System.currentTimeMillis(); + long maxAge = 24L * 60 * 60 * 1000; + File[] files = outputDir.listFiles( + (d, n) -> n.endsWith(".xls")); + if (files == null) { + return; + } + int count = 0; + for (File f : files) { + if (now - f.lastModified() > maxAge) { + if (f.delete()) { + count++; + } + } + } + if (count > 0) { + plugin.getLogger().info( + "[Printer] 清理 " + + count + " 个过期文件"); + } + } + + private void deleteOld(String player) { + File[] old = outputDir.listFiles( + (d, n) -> n.startsWith(player + "_") + && n.endsWith(".xls")); + if (old != null) { + for (File f : old) { + f.delete(); + } + } + } + + // ============ XML工具 ============ + + private void writeXmlHeader(PrintWriter pw, + String sheetName) { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(" "); + pw.println(" "); + } + + private void writeXmlRow(PrintWriter pw, + String[] cells) { + pw.println(" "); + for (String cell : cells) { + pw.println(" " + + "" + + escXml(cell != null ? cell : "") + + ""); + } + pw.println(" "); + } + + private void writeXmlFooter(PrintWriter pw) { + pw.println("
"); + pw.println("
"); + pw.println("
"); + } + + private String escXml(String val) { + if (val == null) { + return ""; + } + return val.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + + private String txTime(Map tx) { + Object t = tx.get("time"); + if (t == null) return ""; + long ms = ((Number) t).longValue(); + return CELL_SDF.format(new Date(ms)); + } + + private String txTypeName( + Map tx) { + String type = (String) tx.get("type"); + return TYPE_NAMES.getOrDefault(type, type); + } + + private int txInt(Map tx, + String key) { + Object v = tx.get(key); + if (v == null) return 0; + return ((Number) v).intValue(); + } + + private String txStr(Map tx, + String key) { + Object v = tx.get(key); + return v != null ? v.toString() : ""; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/CDKManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/CDKManager.java new file mode 100644 index 0000000..e07569f --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/CDKManager.java @@ -0,0 +1,293 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.nio.charset.StandardCharsets; + +public class CDKManager { + + private final Main plugin; + private Connection db; + private final Map pendingInputs + = new ConcurrentHashMap<>(); + + public CDKManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS cdk (" + + "code TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "type TEXT DEFAULT 'bond'," + + "used INTEGER DEFAULT 0," + + "used_by TEXT DEFAULT ''," + + "created_time INTEGER DEFAULT 0," + + "used_time INTEGER DEFAULT 0)"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[CDK] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== CDK兑换(供SDF1调用) ===== + + /** + * 兑换CDK,返回结果字符串 + * @return "success:金额:余额前:余额后" + * 或 "fail:原因" + */ + public String redeem(String code, String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount,type,used FROM cdk " + + "WHERE code=?"); + ps.setString(1, code); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + rs.close(); ps.close(); + return "fail:not_found"; + } + int amount = rs.getInt("amount"); + String type = rs.getString("type"); + int used = rs.getInt("used"); + rs.close(); ps.close(); + + if (used == 1) + return "fail:already_used"; + + if (plugin.getBonds() + .isFrozen(player)) + return "fail:frozen"; + + PreparedStatement up = db.prepareStatement( + "UPDATE cdk SET used=1," + + "used_by=?,used_time=? " + + "WHERE code=?"); + up.setString(1, player); + up.setLong(2, + System.currentTimeMillis()); + up.setString(3, code); + up.executeUpdate(); up.close(); + + if ("bond".equals(type)) { + int bef = plugin.getBonds() + .getBonds(player); + plugin.getBonds().addBonds(player, + amount, + BondManager.TX_REDEEM, + "", "CDK系统", + "CDK兑换: " + code); + int aft = plugin.getBonds() + .getBonds(player); + return "success:" + amount + + ":" + bef + ":" + aft; + } + return "fail:unknown_type"; + } catch (SQLException e) { + return "fail:" + e.getMessage(); + } + } + + // ===== 聊天输入系统 ===== + + public void requestInput(Player p, + String type, + String context) { + CDKInput input = new CDKInput(); + input.type = type; + input.context = context; + input.time = System.currentTimeMillis(); + pendingInputs.put(p.getName(), input); + } + + public boolean isListening(String playerName) { + return pendingInputs + .containsKey(playerName); + } + + public void onChat(Player p, String msg) { + CDKInput input = pendingInputs + .remove(p.getName()); + if (input == null) return; + + if (System.currentTimeMillis() + - input.time > 60000) { + p.sendMessage("§c输入超时"); + return; + } + + if ("cdk".equals(input.type)) { + String result = redeem( + msg.trim(), p.getName()); + if (result != null + && result.startsWith("success:")) { + String[] parts = result.split(":"); + int amt = Integer.parseInt(parts[1]); + int bef = Integer.parseInt(parts[2]); + int aft = Integer.parseInt(parts[3]); + p.sendMessage("§e§l[CDK] §f兑换成功!" + + "获得 §c" + amt + "§f债券"); + p.sendMessage("§7余额: §e" + bef + + " §7-> §a" + aft); + } else { + p.sendMessage("§c§l[CDK] §f" + + (result != null + ? result.replace("fail:", "") + : "无效的兑换码")); + } + return; + } + + if ("econ".equals(input.type)) { + handleEconInput(p, msg.trim(), + input.context); + return; + } + + if ("bond".equals(input.type)) { + handleBondInput(p, msg.trim(), + input.context); + } + } + + private void handleEconInput(Player admin, + String msg, + String target) { + net.milkbowl.vault.economy.Economy econ + = null; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy + .Economy.class); + if (reg != null) + econ = reg.getProvider(); + } catch (Exception ignored) {} + if (econ == null) { + admin.sendMessage("§c经济系统不可用"); + return; + } + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String numStr = + msg.substring(1).trim(); + double amount = + Double.parseDouble(numStr); + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + if (msg.startsWith("-")) { + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp == null) { + admin.sendMessage("§c" + target + + " 不在线"); + return; + } + if (!econ.has(tp, amount)) { + admin.sendMessage("§c余额不足"); + return; + } + econ.withdrawPlayer(tp, amount); + admin.sendMessage("§a扣款成功"); + } else { + econ.depositPlayer(target, amount); + admin.sendMessage("§a到账成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + private void handleBondInput(Player admin, + String msg, + String target) { + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String sign = msg.substring(0, 1); + String rest = + msg.substring(1).trim(); + String[] parts = + rest.split("\\s+", 2); + int amount = Integer.parseInt(parts[0]); + String reason = parts.length >= 2 + ? parts[1].trim() + : "管理员手动调整"; + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + String opName = admin.getName(); + String txType = "-".equals(sign) + ? BondManager.TX_ADMIN_DEDUCT + : BondManager.TX_ADMIN_GIVE; + + if ("-".equals(sign)) { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + if (plugin.getBonds().deductBonds( + target, amount, txType, + "", opName, reason)) { + admin.sendMessage("§a扣除成功"); + } else { + admin.sendMessage("§c债券不足"); + } + } else { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + plugin.getBonds().addBonds(target, + amount, txType, + "", opName, reason); + admin.sendMessage("§a发放成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + public void cancelInput(String player) { + pendingInputs.remove(player); + } + + static class CDKInput { + String type; + String context; + long time; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ChatFilterManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/ChatFilterManager.java new file mode 100644 index 0000000..59cb5de --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ChatFilterManager.java @@ -0,0 +1,612 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatFilterManager { + + private final JavaPlugin plugin; + private final List whitelistUrls = + new ArrayList<>(); + private final Set whitelistPlayers = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Map + punishmentRules = + new LinkedHashMap<>(); + private final Map + punishmentDurations = + new LinkedHashMap<>(); + private final Map + violationCount = + new ConcurrentHashMap<>(); + private final Map + mutedPlayers = + new ConcurrentHashMap<>(); + private final Map messages = + new LinkedHashMap<>(); + private boolean notifyAdmin = true; + private boolean notifyAll = false; + private int muteDuration = 300; + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean v) { + enabled = v; + } + + + private static final Pattern URL_PATTERN = + Pattern.compile( + "(?i)(?:https?://\\S+)" + + "|(?:(?:[a-zA-Z0-9]" + + "(?:[a-zA-Z0-9\\-]*" + + "[a-zA-Z0-9])?\\.)+" + + "[a-zA-Z]{2,})" + + "(?:/\\S*)?"); + private static final Pattern DOT_VARIANTS = + Pattern.compile( + "[\uff0e\u3002\u2025\u2026\u00b7]"); + private static final Pattern COLOR_CODES = + Pattern.compile( + "\u00a7[0-9a-fk-orA-FK-OR]"); + + private static final Set FILE_EXT; + static { + Set s = new HashSet<>( + Arrays.asList( + "txt", "yml", "yaml", + "json", "xml", "java", + "class", "jar", "png", + "jpg", "jpeg", "gif", + "bmp", "svg", "ico", + "webp", "mp3", "mp4", + "avi", "mkv", "wav", + "flac", "ogg", "zip", + "rar", "7z", "tar", + "gz", "cfg", "conf", + "ini", "log", "db", + "sql", "sh", "bat", + "html", "htm", "css", + "js", "md", "pdf", + "dll", "so", "exe", + "bin", "c", "cpp", + "py", "rb", "go", + "rs", "kt", "lua", + "php", "asp", "jsp")); + FILE_EXT = Collections.unmodifiableSet(s); + } + + public ChatFilterManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + // ========== 配置加载 ========== + + public void loadConfig() { + whitelistUrls.clear(); + whitelistPlayers.clear(); + punishmentRules.clear(); + punishmentDurations.clear(); + messages.clear(); + + File f = new File( + plugin.getDataFolder(), "chat.txt"); + if (!f.exists()) writeDefaultFile(f); + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("白名单") + || t.equals("白名单:")) { + section = "whitelist"; + continue; + } + if (t.equals("处罚规则") + || t.equals("处罚规则:")) { + section = "punishment"; + continue; + } + if (t.equals("白名单玩家") + || t.equals("白名单玩家:")) { + section = "players"; + continue; + } + if (t.contains(":")) { + String[] kv = t.split(":", 2); + String k = kv[0].trim(); + String v = kv[1].trim(); + if (equals("启用过滤") + || equals("enabled")) { + enabled = parseBool(v); + continue; + } + + if (k.equals("通知管理员")) { + notifyAdmin = parseBool(v); + continue; + } + if (k.equals("全服通报")) { + notifyAll = parseBool(v); + continue; + } + if (k.equals("禁言时长")) { + try { + muteDuration = + Integer.parseInt(v); + } catch (Exception ignored) { + } + continue; + } + } + switch (section) { + case "whitelist": + whitelistUrls.add( + t.toLowerCase()); + break; + case "punishment": + String[] parts = t.split(":"); + if (parts.length >= 2) { + try { + int cnt = + Integer.parseInt( + parts[0] + .trim()); + String type = + parts[1].trim() + .toLowerCase(); + int dur = + parts.length >= 3 + ? Integer.parseInt( + parts[2] + .trim()) + : muteDuration; + punishmentRules.put( + cnt, type); + punishmentDurations.put( + cnt, dur); + } catch (Exception ignored) { + } + } + break; + case "players": + whitelistPlayers.add(t); + break; + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[Sdf1_chat] 配置加载失败: " + + e.getMessage()); + } + + // 从消息.txt读取chat_开头的消息 + loadMessages(); + } + + private void loadMessages() { + File msgFile = new File( + plugin.getDataFolder(), + "消息.txt"); + if (!msgFile.exists()) return; + try (BufferedReader mr = + new BufferedReader( + new InputStreamReader( + new FileInputStream( + msgFile), + StandardCharsets.UTF_8))) { + String mline; + while ((mline = mr.readLine()) != null) { + mline = mline.trim(); + if (mline.isEmpty() + || mline.startsWith("#")) + continue; + // 支持 = 和 : 两种分隔符 + int eqIdx = mline.indexOf('='); + int coIdx = mline.indexOf(':'); + int splitIdx = -1; + if (eqIdx >= 0 && coIdx >= 0) { + splitIdx = Math.min(eqIdx, coIdx); + } else if (eqIdx >= 0) { + splitIdx = eqIdx; + } else if (coIdx >= 0) { + splitIdx = coIdx; + } + if (splitIdx < 0) continue; + String k = mline.substring(0, + splitIdx).trim(); + String v = mline.substring( + splitIdx + 1).trim(); + if (k.startsWith("chat_")) { + messages.put(k, v); + } + } + } catch (Exception ignored) { + } + } + + + private void writeDefaultFile(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("启用过滤: " + enabled); + pw.println("通知管理员: true"); + pw.println("全服通报: false"); + pw.println("禁言时长: 300"); + pw.println(); + pw.println("白名单:"); + pw.println("*.minecraft.net"); + pw.println("baidu.com"); + pw.println("github.com"); + pw.println(); + pw.println("处罚规则:"); + pw.println("1:warn:0"); + pw.println("2:warn:0"); + pw.println("3:mute:300"); + pw.println("5:mute:600"); + pw.println("8:kick:0"); + pw.println("10:ban:0"); + pw.println(); + pw.println("白名单玩家:"); + } catch (IOException ignored) { + } + } + + public void saveConfig() { + File f = new File( + plugin.getDataFolder(), "chat.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("通知管理员: " + notifyAdmin); + pw.println("全服通报: " + notifyAll); + pw.println("禁言时长: " + muteDuration); + pw.println(); + pw.println("白名单:"); + for (String u : whitelistUrls) + pw.println(u); + pw.println(); + pw.println("处罚规则:"); + for (Map.Entry entry + : punishmentRules.entrySet()) { + int dur = punishmentDurations + .getOrDefault(entry.getKey(), + muteDuration); + pw.println(entry.getKey() + ":" + + entry.getValue() + + ":" + dur); + } + pw.println(); + pw.println("白名单玩家:"); + for (String p : whitelistPlayers) + pw.println(p); + } catch (IOException e) { + plugin.getLogger().severe( + "[Sdf1_chat] 保存失败: " + + e.getMessage()); + } + } + + // ========== 消息 ========== + + public String msg(String key, + String... args) { + String template = messages.getOrDefault( + key, key); + for (int i = 0; i < args.length - 1; + i += 2) { + template = template.replace( + "{" + args[i] + "}", + args[i + 1]); + } + return template; + } + + // ========== URL检测 ========== + + private String normalizeMessage(String msg) { + msg = COLOR_CODES.matcher(msg) + .replaceAll(""); + msg = DOT_VARIANTS.matcher(msg) + .replaceAll("."); + return msg; + } + + public List extractUrls( + String message) { + String normalized = + normalizeMessage(message); + List urls = new ArrayList<>(); + Matcher m = URL_PATTERN + .matcher(normalized); + while (m.find()) { + String url = m.group().toLowerCase(); + url = url.replaceAll( + "[\\s.,;!?,。;!?、)》」』】" + + "\\]\\[\\((]+$", + ""); + if (!url.isEmpty()) urls.add(url); + } + return urls; + } + + public boolean isWhitelisted(String url) { + String clean = url + .replaceAll("^https?://", "") + .toLowerCase(); + int colon = clean.indexOf(':'); + int slash = clean.indexOf('/'); + if (colon > 0 && (slash < 0 + || colon < slash)) { + clean = clean.substring(0, colon); + } + slash = clean.indexOf('/'); + if (slash > 0) { + clean = clean.substring(0, slash); + } + for (String entry : whitelistUrls) { + String e = entry.toLowerCase().trim(); + if (e.startsWith("*.")) { + String suffix = e.substring(1); + if (clean.endsWith(suffix) + || clean.equals( + suffix.substring(1))) + return true; + } else { + if (clean.equals(e) + || clean.endsWith( + "." + e)) + return true; + } + } + return false; + } + + public boolean isLikelyDomain(String url) { + int lastDot = url.lastIndexOf('.'); + if (lastDot < 0 + || lastDot >= url.length() - 1) + return false; + String ext = url.substring(lastDot + 1) + .toLowerCase(); + return !FILE_EXT.contains(ext); + } + + // ========== 玩家操作 ========== + + public boolean isMuted(String name) { + Long expiry = mutedPlayers.get(name); + if (expiry == null) return false; + if (System.currentTimeMillis() + >= expiry) { + mutedPlayers.remove(name); + return false; + } + return true; + } + + public String fmtDuration(int seconds) { + if (seconds <= 0) return "0秒"; + int day = seconds / 86400; + int hr = (seconds % 86400) / 3600; + int min = (seconds % 3600) / 60; + int sec = seconds % 60; + StringBuilder sb = new StringBuilder(); + if (day > 0) sb.append(day).append("天"); + if (hr > 0) + sb.append(hr).append("小时"); + if (min > 0) + sb.append(min).append("分"); + if (sec > 0 || sb.length() == 0) + sb.append(sec).append("秒"); + return sb.toString(); + } + + public void applyPunishment(Player player, + int violation) { + String type = "warn"; + int duration = muteDuration; + int maxRule = 0; + for (Map.Entry entry + : punishmentRules.entrySet()) { + if (violation >= entry.getKey() + && entry.getKey() > maxRule) { + maxRule = entry.getKey(); + type = entry.getValue(); + duration = punishmentDurations + .getOrDefault( + entry.getKey(), + muteDuration); + } + } + if (type.equals("mute")) { + if (isMuted(player.getName())) + return; + mutedPlayers.put(player.getName(), + System.currentTimeMillis() + + (long) duration + * 1000L); + } + final String fType = type; + final int fDur = duration; + final int fV = violation; + final String fName = player.getName(); + Bukkit.getScheduler().runTask(plugin, + () -> { + Player p = Bukkit.getPlayer(fName); + if (p == null) return; + switch (fType) { + case "warn": + // 警告不处罚,只提示 + break; + case "mute": + p.sendMessage( + msg("chat_muted")); + break; + case "kick": + p.kickPlayer( + msg("chat_muted")); + break; + case "ban": + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban " + fName + + " §c多次发送违规链接"); + break; + case "banip": + InetSocketAddress addr = + p.getAddress(); + if (addr != null) { + String ip = addr + .getAddress() + .getHostAddress(); + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban-ip " + ip + + " §c发送违规链接"); + } + break; + } + }); + } + + public void unmutePlayer(String name) { + mutedPlayers.remove(name); + } + + public void resetPlayer(String name) { + mutedPlayers.remove(name); + violationCount.remove(name); + } + + public boolean isPlayerWhitelisted( + String name) { + return whitelistPlayers.contains(name); + } + + public boolean isNotifyAdmin() { + return notifyAdmin; + } + + public boolean isNotifyAll() { + return notifyAll; + } + + public int getViolationCount(String name) { + return violationCount + .getOrDefault(name, 0); + } + + public void incrementViolation( + String name) { + violationCount.put(name, + getViolationCount(name) + 1); + } + + public List getWhitelistUrls() { + return whitelistUrls; + } + + public Set getWhitelistPlayers() { + return whitelistPlayers; + } + + public Map + getPunishmentRules() { + return punishmentRules; + } + + public Map + getPunishmentDurations() { + return punishmentDurations; + } + + public Map + getMutedPlayers() { + return mutedPlayers; + } + + public void setNotifyAdmin(boolean v) { + notifyAdmin = v; + } + + public void setNotifyAll(boolean v) { + notifyAll = v; + } + + public void setMuteDuration(int v) { + muteDuration = v; + } + + public void addUrl(String url) { + whitelistUrls.add( + url.toLowerCase().trim()); + saveConfig(); + } + + public boolean removeUrl(String url) { + boolean r = whitelistUrls.remove( + url.toLowerCase().trim()); + saveConfig(); + return r; + } + + public void addWhitelistPlayer( + String name) { + whitelistPlayers.add(name); + saveConfig(); + } + + public boolean removeWhitelistPlayer( + String name) { + boolean r = + whitelistPlayers.remove(name); + saveConfig(); + return r; + } + + public void cleanExpired() { + long now = System.currentTimeMillis(); + mutedPlayers.entrySet().removeIf( + e -> e.getValue() <= now); + } + + private boolean parseBool(String v) { + String s = v.toLowerCase().trim(); + return s.equals("true") + || s.equals("on") + || s.equals("yes") + || s.equals("1") + || s.contains("开启") + || s.contains("启用") + || s.contains("是"); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ChatInputManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/ChatInputManager.java new file mode 100644 index 0000000..49f53fd --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ChatInputManager.java @@ -0,0 +1,842 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class ChatInputManager { + + public enum InputType { + NONE, + CHANGE_PWD_STEP1, + CHANGE_PWD_STEP2, + SET_EMAIL, + SET_EMAIL_VERIFY, + ADMIN_AUTH, + ADMIN_SET_POINTS, + ADMIN_SET_PWD, + ADMIN_DELETE_CONFIRM, + ADMIN_SET_AFK_TIME, + SMTP_CONFIG, + SMTP_VERIFY, + BACK_CHECKIN, + INVITE_INPUT_CODE, + TICKET_TITLE, + TICKET_DESC, + TICKET_SCORE, + TICKET_REJECT, + TICKET_ASSIGN, + TICKET_INVITE_PROVIDER, + TICKET_REMOVE_PROVIDER, + TICKET_COMPLETE, + TICKET_REPORT_ISSUE, + TICKET_REPLY, + TICKET_ADMIN_TYPE, + TICKET_ADMIN_TITLE, + TICKET_ADMIN_DESC, + TICKET_ADMIN_PROVIDER, + BACK_CHECK_ECONOMY_CONFIRM, + USER_MGMT_SEARCH, + + } + + public static class PlayerState { + public InputType type = InputType.NONE; + public String targetPlayer = ""; + public String ticketType = ""; + public String ticketTitle = ""; + public String ticketDesc = ""; + public int ticketId = 0; + public int smtpStep = 0; + public int scoreValue = 0; + + } + + private final Map states = + new HashMap<>(); + private Main plugin; + + public PlayerState getState(Player p) { + return states.computeIfAbsent( + p.getName(), + k -> new PlayerState()); + } + + public void reset(Player p) { + states.remove(p.getName()); + } + public boolean isInFlow(Player p) { + PlayerState state = states.get(p.getName()); + return state != null + && state.type != InputType.NONE; + } + + public void handleInput(Player p, String msg, + Main mainPlugin) { + if (this.plugin == null) + this.plugin = mainPlugin; + PlayerState state = getState(p); + ConfigManager config = + mainPlugin.getConfig2(); + DatabaseManager db = mainPlugin.getDb(); + + switch (state.type) { + + case CHANGE_PWD_STEP1: { + String name = p.getName(); + String salt = (String) db.getField( + name, "password_salt"); + String hash = + PasswordUtils.hash(msg, salt); + boolean mainOk = + db.checkPassword(name, hash); + if (mainOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "main"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + boolean tempOk = + db.checkPasswordOrTemp( + name, hash); + if (tempOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "temp"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + p.sendMessage(config.msg( + "password_wrong")); + return; + } + + case CHANGE_PWD_STEP2: { + String name = p.getName(); + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String oldHash = + (String) db.getField(name, + "password_hash"); + String oldSalt = + (String) db.getField(name, + "password_salt"); + String newSalt = + PasswordUtils.generateSalt(); + String newHash = + PasswordUtils.hash( + msg, newSalt); + if (newHash.equals(oldHash)) { + p.sendMessage(config.msg( + "password_same")); + reset(p); + return; + } + mainPlugin.recordPasswordChange( + name, oldHash, oldSalt); + db.setField(name, + "password_hash", newHash); + db.setField(name, + "password_salt", newSalt); + mainPlugin + .getNeedsPasswordChange() + .remove(name); + p.sendMessage(config.msg( + "password_changed")); + reset(p); + return; + } + + case SET_EMAIL: { + if (!msg.contains("@") + || !msg.contains(".")) { + p.sendMessage(config.msg( + "email_format_error")); + reset(p); + return; + } + String oldEmail = (String) + db.getField(p.getName(), + "email"); + String emailAddr = msg.trim(); + // 已绑定邮箱:验证登录密码 + if (oldEmail != null + && !oldEmail.isEmpty()) { + state.targetPlayer = emailAddr; + state.type = + InputType.SET_EMAIL_VERIFY; + state.ticketTitle = + "need_pwd"; + p.sendMessage( + "§e您已绑定邮箱: " + + oldEmail); + p.sendMessage( + "§e请验证登录密码以确认修改:"); + return; + } + // 未绑定邮箱:直接发验证码 + String code = String.valueOf( + (int) (Math.random() + * 900000 + + 100000)); + state.targetPlayer = emailAddr; + state.ticketTitle = code; + state.type = + InputType.SET_EMAIL_VERIFY; + final String to = emailAddr; + final String c = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + boolean sent = mainPlugin + .getEmail() + .sendVerifyCode( + to, c); + Bukkit.getScheduler() + .runTask(mainPlugin, + () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + reset(fp); + } + }); + }); + return; + } + + + case SET_EMAIL_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§c邮箱设置已取消"); + reset(p); + return; + } + String expected = + state.ticketTitle; + if (msg.trim().equals(expected)) { + db.setField(p.getName(), + "email", + state.targetPlayer); + p.sendMessage( + "§a邮箱已设置: " + + state.targetPlayer); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case ADMIN_AUTH: { + if (msg.equals( + config.adminPassword)) { + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openAdmin(p)); + } else { + p.sendMessage( + "§c管理密码错误"); + } + reset(p); + return; + } + + case ADMIN_SET_POINTS: { + try { + int pts = Integer.parseInt( + msg.trim()); + db.setField( + state.targetPlayer, + "points", pts); + p.sendMessage("§a已将 " + + state.targetPlayer + + " 积分设为 " + pts); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + String tgt = + state.targetPlayer; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openUserDetail( + p, tgt)); + reset(p); + return; + } + + case ADMIN_SET_PWD: { + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String tgt = state.targetPlayer; + String salt = + PasswordUtils + .generateSalt(); + String hash = + PasswordUtils.hash( + msg, salt); + db.setField(tgt, + "temp_password", hash); + db.setField(tgt, "temp_pw_expire", + System.currentTimeMillis() + + 300000L); + db.setField(tgt, + "temp_pw_used", 0); + p.sendMessage("§a已为 " + tgt + + " 设置临时密码"); + Player tp = + Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + tp.kickPlayer( + "§c管理员已设置临时密码," + + "请使用临时密码登录"); + // 邮件通知 + String emailAddr = (String) + db.getField(tgt, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String time = + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()); + String body = "玩家 " + tgt + + ":\n您的密码已于 " + + time + + " 被管理员重置为临时密码。\n" + + "如果是本人操作请忽略。\n" + + "如非本人操作请立即联系管理员。"; + final String to = emailAddr; + final String fBody = body; + final String fName = tgt; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> + mainPlugin.getEmail() + .sendTempPassword( + to, + fName, + fBody)); + } + reset(p); + return; + } + + case ADMIN_DELETE_CONFIRM: { + if (msg.equals( + state.targetPlayer)) { + db.deleteUser( + state.targetPlayer); + p.sendMessage("§a已删除 " + + state.targetPlayer); + Player tgt = + Bukkit.getPlayer( + state.targetPlayer); + if (tgt != null + && tgt.isOnline()) + tgt.kickPlayer( + "§c账号已被删除"); + } else { + p.sendMessage( + "§c输入不匹配,已取消"); + } + reset(p); + return; + } + + case ADMIN_SET_AFK_TIME: { + try { + int minutes = + Integer.parseInt( + msg.trim()); + config.afkTimeout = + minutes * 60; + config.saveSettings(); + p.sendMessage( + "§a挂机时长已设为 " + + minutes + + " 分钟"); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + reset(p); + return; + } + + case SMTP_CONFIG: { + String m = msg.trim(); + if ("000".equals(m) + || "取消取消取消" + .equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if ("取消".equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + int step = state.smtpStep; + if (step <= 1) { + if (!"0".equals(m)) + config.setSmtp( + "smtp地址", m); + state.smtpStep = 2; + p.sendMessage("§e当前端口: " + + config.getSmtp( + "smtp端口")); + p.sendMessage( + "§e输入端口(0跳过):"); + } else if (step == 2) { + if (!"0".equals(m)) + config.setSmtp( + "smtp端口", m); + state.smtpStep = 3; + p.sendMessage( + "§e输入发件邮箱:"); + } else if (step == 3) { + if (!"0".equals(m)) + config.setSmtp( + "smtp账号", m); + state.smtpStep = 4; + p.sendMessage( + "§e输入SMTP密码(0跳过):"); + } else if (step == 4) { + if (!"0".equals(m)) + config.setSmtp( + "smtp密码", m); + state.smtpStep = 5; + p.sendMessage( + "§e输入接收邮箱(留空发到发件邮箱):"); + } else if (step == 5) { + String recv = m; + if (recv.isEmpty() + || "0".equals(recv)) + recv = config.getSmtp( + "smtp账号"); + config.setSmtp( + "验证码接收邮箱", recv); + if (!config + .canSendSmtpVerify()) { + p.sendMessage( + "§c发送间隔不足1分钟"); + reset(p); + return; + } + String code = config + .generateSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String acct = config.getSmtp( + "smtp账号"); + if (acct.isEmpty() + || addr.isEmpty()) { + p.sendMessage( + "§c地址或账号为空"); + reset(p); + return; + } + config.recordSmtpSendTime(); + final String fRecv = recv; + final String fCode = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> fp.sendMessage( + "§e正在发送验证码...")); + boolean sent = + mainPlugin.getEmail() + .sendVerifyCode( + fRecv, + fCode); + final boolean fSent = sent; + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> { + if (fSent) { + state.type = + InputType + .SMTP_VERIFY; + state.smtpStep = 0; + fp.sendMessage( + "§a验证码已发送到 " + + fRecv); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败,查看控制台"); + reset(fp); + } + }); + }); + } + return; + } + + case SMTP_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if (config.checkSmtpVerifyCode( + msg.trim())) { + config.saveSmtp(); + config.clearSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String port = config.getSmtp( + "smtp端口"); + String acct = config.getSmtp( + "smtp账号"); + String recv = config.getSmtp( + "验证码接收邮箱"); + boolean ssl = config.smtpSsl; + p.sendMessage("§a§l=== SMTP配置完成 ==="); + p.sendMessage("§7地址: §f" + addr); + p.sendMessage("§7端口: §f" + port); + p.sendMessage("§7账号: §f" + acct); + p.sendMessage("§7接收: §f" + recv); + p.sendMessage("§7加密: §f" + ssl); + p.sendMessage("§a§l===================="); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case BACK_CHECKIN: { + String result = + mainPlugin.getCheckIn() + .backCheckIn(p, msg); + if (result == null) { + // 等待选择(NEED_CONFIRM) + return; + } + if (result.startsWith( + "NEED_CONFIRM:")) { + String[] parts = + result.split(":"); + // 存储到Main的Map + mainPlugin.pendingBackCheck.put( + p.getName(), + parts[1] + ":" + parts[2]); + return; + } + if (result.startsWith("§c")) { + p.sendMessage(result); + } else { + p.sendMessage(result); + } + reset(p); + return; + } + + + case INVITE_INPUT_CODE: { + String code = msg.trim(); + String myName = p.getName(); + String myCode = + (String) db.getField( + myName, + "invite_code"); + if (code.isEmpty()) { + p.sendMessage( + "§c邀请码不能为空"); + reset(p); + return; + } + if (code.equals(myCode)) { + p.sendMessage( + "§c不能用自己邀请码"); + reset(p); + return; + } + boolean found = false; + for (Map u : + db.getAllUsers()) { + String uc = (String) + u.getOrDefault( + "invite_code", ""); + if (code.equals(uc)) { + String inv = (String) + u.get("player_name"); + db.setField(myName, + "invited_by", inv); + db.addPoints(inv, 50); + p.sendMessage( + "§a邀请成功!邀请人: " + + inv); + Player ip = + Bukkit.getPlayer(inv); + if (ip != null + && ip.isOnline()) + ip.sendMessage("§a" + + myName + + " 使用了您的邀请码"); + found = true; + break; + } + } + if (!found) + p.sendMessage( + "§c邀请码不存在"); + reset(p); + return; + } + + case TICKET_TITLE: { + state.ticketTitle = msg; + state.type = + InputType.TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return; + } + + case TICKET_DESC: { + String tt = state.ticketType; + String tl = state.ticketTitle; + reset(p); + mainPlugin.getTicket() + .submitTicket( + p, tt, tl, msg); + return; + } + + case TICKET_SCORE: { + int tid = state.ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + reset(p); + mainPlugin.getTicket() + .confirmAndScore( + p, tid, sc); + } catch ( + NumberFormatException e) { + reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return; + } + + case TICKET_REJECT: { + int tid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .rejectTicket( + p, tid, msg); + return; + } + + case TICKET_ASSIGN: { + int tid = state.ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + reset(p); + mainPlugin.getTicket() + .assignToProvider( + p, tid, prov, + price); + final int fTid = tid; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getTicket() + .openDetail( + p, fTid)); + return; + } + + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return; + } + db.addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return; + } + + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.isServiceProvider(tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return; + } + db.removeServiceProvider(tgt); + p.sendMessage("§a已移除 " + tgt); + return; + } + + case TICKET_COMPLETE: { + try { + int tid = Integer.parseInt( + msg.trim()); + mainPlugin.getTicket() + .completeAsProvider( + p, tid); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效工单号"); + } + reset(p); + return; + } + + case TICKET_REPORT_ISSUE: { + String report = msg.trim(); + reset(p); + p.sendMessage("§e异常已上报"); + String tag = config.adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§c§l[工单异常] §f" + + p.getName() + + ": " + report); + } + } + return; + } + + case TICKET_REPLY: { + int replyTid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .replyTicket( + p, replyTid, msg); + return; + } + + case TICKET_ADMIN_TYPE: { + state.ticketType = msg.trim(); + state.type = + InputType + .TICKET_ADMIN_TITLE; + p.sendMessage( + "§e请输入工单标题:"); + return; + } + + case TICKET_ADMIN_TITLE: { + state.ticketTitle = msg; + state.type = + InputType + .TICKET_ADMIN_DESC; + p.sendMessage( + "§e请输入工单描述:"); + return; + } + + case TICKET_ADMIN_DESC: { + state.targetPlayer = msg; + state.type = + InputType + .TICKET_ADMIN_PROVIDER; + p.sendMessage( + "§e输入服务商名和价格:"); + p.sendMessage( + "§7例: playerA 20"); + return; + } + + case TICKET_ADMIN_PROVIDER: { + String[] parts2 = msg.trim() + .split("\\s+"); + String prov2 = parts2[0]; + int price2 = 0; + if (parts2.length >= 2) { + try { + price2 = Integer.parseInt( + parts2[1]); + } catch ( + NumberFormatException e) { + } + } + String t2 = state.ticketType; + String ti2 = state.ticketTitle; + String d2 = state.targetPlayer; + reset(p); + mainPlugin.getTicket() + .adminCreateAndAssign( + p, t2, ti2, + d2, prov2, price2); + return; + } + + default: + reset(p); + return; + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/CheckInManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/CheckInManager.java new file mode 100644 index 0000000..b594b0f --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/CheckInManager.java @@ -0,0 +1,302 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class CheckInManager { + + private final Main plugin; + + public CheckInManager(Main plugin) { + this.plugin = plugin; + } + + // ===== 今日是否已签到 ===== + public boolean isCheckedInToday( + String name) { + String last = (String) plugin.getDb() + .getField(name, + "last_checkin_date"); + String today = new SimpleDateFormat( + "yyyy-MM-dd") + .format(new Date()); + return today.equals(last); + } + + // ===== 签到 ===== + // ===== 签到 ===== + public String checkIn(Player p) { + String name = p.getName(); + if (isCheckedInToday(name)) { + return plugin.getConfig2() + .msg("checkin_already"); + } + + Object streakObj = plugin.getDb() + .getField(name, "checkin_streak"); + int streak = streakObj != null + ? ((Number) streakObj).intValue() + : 0; + + String lastDate = (String) plugin.getDb() + .getField(name, "last_checkin_date"); + String today = new SimpleDateFormat("yyyy-MM-dd") + .format(new Date()); + + if (lastDate != null && !lastDate.isEmpty()) { + try { + java.util.Date lastD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(lastDate); + java.util.Date todayD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(today); + long diff = todayD.getTime() + - lastD.getTime(); + if (diff > 86400000L * 2) { + streak = 0; + } else if (diff > 86400000L) { + streak++; + } + } catch (Exception ignored) { + } + } + + if (streak == 0) streak = 1; + else streak++; + + double multi = 1.0; + if (streak >= 21) multi = 1.5; + else if (streak >= 14) multi = 1.3; + else if (streak >= 7) multi = 1.1; + + int basePoints = + plugin.getConfig2().checkinPoints; + int points = (int) (basePoints * multi); + + plugin.getDb().addPoints(name, points); + plugin.getDb().setField(name, + "checkin_streak", streak); + plugin.getDb().setField(name, + "last_checkin_date", today); + + Object totalObj = plugin.getDb() + .getField(name, "total_checkin_days"); + int total = totalObj != null + ? ((Number) totalObj).intValue() + : 0; + plugin.getDb().setField(name, + "total_checkin_days", total + 1); + + // ★ 债券盲盒奖励(6参数版,写入流水)★ + int bondMin = + plugin.getConfig2().checkinBondMin; + int bondMax = + plugin.getConfig2().checkinBondMax; + if (bondMin <= 0) bondMin = 1; + if (bondMax < bondMin) bondMax = bondMin; + int bondReward = bondMin + + new java.util.Random() + .nextInt(bondMax - bondMin + 1); + plugin.getBonds().addBonds(name, bondReward, + "daily_sign", "", "签到系统", + "每日签到连续" + streak + "天"); + + return plugin.getConfig2() + .msg("checkin_success") + .replace("{points}", + String.valueOf(points)) + .replace("{streak}", + String.valueOf(streak)) + .replace("{multi}", + String.valueOf(multi)) + + " §e+" + bondReward + "债券"; + } + + // ===== 获取连续天数 ===== + public int getStreak(String name) { + Object val = plugin.getDb() + .getField(name, + "checkin_streak"); + if (val instanceof Number) { + return ((Number) val) + .intValue(); + } + return 0; + } + // 标准化日期 + private String normalizeDate(String input) { + String s = input.trim().replace("/", "-"); + if (s.matches("\\d{8}")) { + return s.substring(0, 4) + "-" + + s.substring(4, 6) + "-" + + s.substring(6, 8); + } + return s; + } + // ===== 补签入口 ===== + public String backCheckIn( + Player p, String dateStr) { + String name = p.getName(); + String normalized = + normalizeDate(dateStr); + + // 严格日期验证 + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd"); + sdf.setLenient(false); + java.util.Date target; + try { + target = sdf.parse(normalized); + } catch (Exception ex) { + return "§c无效日期: " + dateStr; + } + String reformat = sdf.format(target); + if (!reformat.equals(normalized)) { + return "§c无效日期: " + dateStr; + } + + String today = sdf.format(new Date()); + if (normalized.compareTo(today) >= 0) { + return "§c今天不需要补签"; + } + + String threeDaysAgo = getPastDate(3); + if (normalized.compareTo( + threeDaysAgo) < 0) { + return "§c只能补签3天内" + + "(最多到 " + threeDaysAgo + ")"; + } + + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + boolean alreadyBacked = false; + if (!existing.isEmpty()) { + for (String d : existing.split(",")) { + if (normalizeDate(d.trim()) + .equals(normalized)) { + alreadyBacked = true; + break; + } + } + } + if (alreadyBacked) { + return "§c该日期已补签: " + normalized; + } + + Object normalCheck = + plugin.getDb().getField( + name, "last_checkin_date"); + String lastCheckin = normalCheck != null + ? normalCheck.toString() : ""; + if (lastCheckin.equals(normalized)) { + return "§c该日期已签到: " + normalized; + } + + // ★ 补签消耗:3债券 + 10积分 + // 积分不够时:用 9债券(3倍)代替 ★ + int bondCost = 3; + int pointCost = 10; + int bondAltCost = 9; // 3倍债券替代 + + int myBonds = plugin.getBonds() + .getBonds(name); + Object ptsObj = plugin.getDb() + .getField(name, "points"); + int myPoints = ptsObj != null + ? ((Number) ptsObj).intValue() + : 0; + + + // 方案A:3债券 + 10积分 + boolean canA = myBonds >= bondCost + && myPoints >= pointCost; + // 方案B:9债券(积分不够时) + boolean canB = myBonds >= bondAltCost + && myPoints < pointCost; + + if (!canA && !canB) { + if (myPoints < pointCost + && myBonds < bondAltCost) { + return "§c补签费用不足" + + "(需3债券+10积分," + + "或9债券代替)" + + " 当前: " + + myBonds + "债券 " + + myPoints + "积分"; + } + if (myBonds < bondCost) { + return "§c债券不足(需" + + bondCost + "枚)"; + } + return "§c积分不足(需" + + pointCost + "分)"; + } + + // 执行扣款 + if (canA) { + // 方案A:3债券 + 10积分 + plugin.getBonds().deductBonds( + name, bondCost, + "admin_deduct", "", + "补签系统", "补签扣款"); + plugin.getDb().deductPoints( + name, pointCost); + } else { + // 方案B:9债券代替 + plugin.getBonds().deductBonds( + name, bondAltCost, + "admin_deduct", "", + "补签系统", "补签扣款(债券替代)"); + } + + doBackCheckIn(p, normalized); + + String costMsg = canA + ? bondCost + "债券+" + pointCost + "积分" + : bondAltCost + "债券(替代)"; + return "§a补签成功!日期: " + + normalized + + " §7消耗: " + costMsg; + } + + + + // ===== 执行补签 ===== + // ===== 执行补签 ===== + public void doBackCheckIn( + Player p, String dateStr) { + String name = p.getName(); + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + String updated = existing.isEmpty() + ? dateStr + : existing + "," + dateStr; + plugin.getDb().setField(name, + "backcheck_dates", updated); + int streak = getStreak(name); + plugin.getDb().setField(name, + "checkin_streak", streak + 1); + // ★ 补签不发放奖励,只记录 + } + + // ===== 获取N天前日期 ===== + private String getPastDate( + int daysAgo) { + Calendar cal = + Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, + -daysAgo); + return new SimpleDateFormat( + "yyyy-MM-dd") + .format(cal.getTime()); + } +} \ No newline at end of file diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/CommissionManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/CommissionManager.java new file mode 100644 index 0000000..cb24687 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/CommissionManager.java @@ -0,0 +1,227 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.logging.Logger; + +public class CommissionManager { + + private final Main main; + private final Logger logger; + private final List rules = + new ArrayList<>(); + + public CommissionManager(Main main) { + this.main = main; + this.logger = main.getLogger(); + loadRules(); + } + + public void loadRules() { + rules.clear(); + File f = new File( + main.getDataFolder(), + "提成规则.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader br = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) + continue; + // 格式: 邀请人数:提成百分比 + // 如 "5:15" = 邀请5人提成15% + String[] parts = + line.split(":"); + if (parts.length >= 2) { + try { + int count = Integer.parseInt( + parts[0].trim()); + double rate = Double + .parseDouble( + parts[1] + .trim()) / 100.0; + rules.add(new double[]{ + count, rate}); + } catch (Exception ignored) { + } + } + } + // 按人数排序(从大到小) + rules.sort((a, b) -> + Double.compare(b[0], a[0])); + logger.info( + "[Commission] 加载 " + + rules.size() + " 条提成规则"); + } catch (Exception e) { + logger.warning( + "[Commission] 加载失败: " + + e.getMessage()); + } + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 邀请提成规则"); + pw.println("# 格式: 邀请人数:提成百分比"); + pw.println("# 邀请10人提成20%"); + pw.println("10:20"); + pw.println("# 邀请5人提成15%"); + pw.println("5:15"); + pw.println("# 邀请1人提成10%"); + pw.println("1:10"); + } catch (IOException ignored) { + } + } + + /** + * 获取邀请人的提成比例 + * 根据已邀请人数匹配最高规则 + */ + public double getCommissionRate( + String inviter) { + int inviteCount = getInviteCount( + inviter); + for (double[] rule : rules) { + if (inviteCount >= (int) rule[0]) { + return rule[1]; + } + } + return 0; + } + + /** + * 获取邀请人数 + */ + public int getInviteCount( + String inviter) { + try { + Object val = + main.getDb().getField( + inviter, + "monthly_invite_count"); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + Object total = + main.getDb().getField( + inviter, + "total_invite_count"); + if (total instanceof Number) { + return ((Number) total).intValue(); + } + } catch (Exception ignored) { + } + return 0; + } + + /** + * 发放提成 + * 被邀请人获得奖励时,邀请人获得提成 + */ + public void payCommission( + String invitee, + double originalAmount) { + // 查找邀请人 + String inviter = getInviter(invitee); + if (inviter == null + || inviter.isEmpty()) return; + + double rate = getCommissionRate( + inviter); + if (rate <= 0) return; + + double commission = + originalAmount * rate; + if (commission <= 0) return; + + // 通过 Vault 发放 + var rsp = main.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy.Economy.class); + if (rsp == null) return; + net.milkbowl.vault.economy.Economy econ = + rsp.getProvider(); + if (econ == null) return; + + econ.depositPlayer(inviter, commission); + + // 通知邀请人 + Player inviterP = + main.getServer() + .getPlayer(inviter); + if (inviterP != null + && inviterP.isOnline()) { + inviterP.sendMessage( + "§a§l[邀请提成] §f" + + invitee + " 完成任务," + + "你获得 §e" + + String.format("%.2f", + commission) + + " §f游戏币提成 (" + + (int)(rate * 100) + "%)"); + } + + logger.info( + "[Commission] 提成: " + + inviter + " ← " + + invitee + " | " + + String.format("%.2f", + originalAmount) + + " × " + + (int)(rate * 100) + "% = " + + String.format("%.2f", + commission)); + } + + /** + * 获取邀请人 + */ + private String getInviter(String player) { + try { + Object val = + main.getDb().getField( + player, + "invited_by"); + if (val != null + && !val.toString() + .isEmpty()) { + return val.toString(); + } + } catch (Exception ignored) { + } + return null; + } + + /** + * 判断金额是否包含经济内容 + */ + public boolean hasEconomyContent( + String text) { + String t = text.toLowerCase(); + return t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额"); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ConfigManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/ConfigManager.java new file mode 100644 index 0000000..a15ada0 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ConfigManager.java @@ -0,0 +1,372 @@ +package Sdf1_login; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class ConfigManager { + + private final File dataFolder; + private final Map messages + = new LinkedHashMap<>(); + private final Map smtpSettings + = new LinkedHashMap<>(); + + // ===== 公共字段 ===== + public String adminTag = "admin"; + public String adminPassword = "qweasd"; + public int maxAccountsPerIP = 3; + public String approvalMode = "manual"; + public int autoApproveDelayMinutes = 30; + public int loginTimeout = 60; + public boolean afkEnabled = false; + public int afkTimeout = 300; + public boolean smtpSsl = true; + public String smtpVerifyCode = ""; + public long smtpVerifyExpire = 0; + public long smtpLastSendTime = 0; + public String smtpConfiguringPlayer = ""; + public String broadcastServerUrl = ""; + public int httpPort = 8080; + public String garbageTag = "admin"; + + public double checkinRewardFixed = 0; + public double checkinRewardMin = 0; + public double checkinRewardMax = 0; + public boolean checkinGivePoints = true; + public int checkinPoints = 10; + public String checkinRewardType = "none"; + public int backCheckPointMultiplier = 5; + public int checkinBondMin = 1; + public int checkinBondMax = 3; + + public double providerPointsPerScore = 2.0; + public int baseEconomyReward = 100; + public double providerEconomyPerScore = 50.0; + public int requesterPointsReward = 10; + + public boolean garbageEnabled = true; + public int garbageInterval = 300; + public int garbageMaxRounds = 1; + + // ★ 构造函数接收 File,和 Main.java 调用一致 ★ + public ConfigManager(File dataFolder) { + this.dataFolder = dataFolder; + } + public String rewardChannel = "债券"; // bonds = 债券,economy = Vault经济 + + + // ==================== 消息 ==================== + + public void loadMessages() { + messages.clear(); + File file = new File(dataFolder, "消息.txt"); + if (!file.exists()) createDefaultMessages(file); + loadMap(file, messages); + } + + public String msg(String key) { + String val = messages.getOrDefault(key, key); + val = val.replace("", "&l"); + val = val.replace("", "&r"); + val = val.replace("
", "\n"); + val = val.replace("\\n", "\n"); + val = val.replaceAll( + "&([0-9a-fk-orA-FK-OR])", "§$1"); + return val; + } + + private void createDefaultMessages(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 消息配置 ====="); + L.add("login_timeout=§c登录超时"); + L.add("not_registered=§e您尚未注册"); + L.add("not_logged_in=§e您尚未登录"); + L.add("already_registered=§c您已注册过"); + L.add("already_logged_in=§c您已登录"); + L.add("reg_success=§a注册成功!"); + L.add("login_success=§a登录成功!"); + L.add("login_failed=§c密码错误"); + L.add("password_wrong=§c密码错误"); + L.add("password_changed=§a密码修改成功!"); + L.add("password_format_error=§c密码需6位以上含大小写和数字"); + L.add("password_same=§c新密码不能与旧密码相同"); + L.add("checkin_already=§c您今日已签到"); + L.add("checkin_success=§a签到成功!获得 {points} 积分"); + L.add("chat_muted=§c你已被禁言"); + L.add("未知参数=§c未知参数或权限不足"); + writeLines(f, L); + } + + // ==================== SMTP ==================== + + public void loadSmtp() { + smtpSettings.clear(); + File file = new File(dataFolder, "SMTP设置.txt"); + if (!file.exists()) createDefaultSmtp(file); + loadMap(file, smtpSettings); + } + + public String getSmtp(String key) { + return smtpSettings.getOrDefault(key, ""); + } + + public void setSmtp(String key, String value) { + smtpSettings.put(key, value); + } + + public void saveSmtp() { + File file = new File(dataFolder, "SMTP设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + for (Map.Entry e + : smtpSettings.entrySet()) { + L.add(e.getKey() + "=" + e.getValue()); + } + writeLines(file, L); + } + + private void createDefaultSmtp(File f) { + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + L.add("smtp地址=smtp.example.com"); + L.add("smtp端口=465"); + L.add("smtp账号="); + L.add("smtp密码="); + L.add("发件人名称=Sdf1_login"); + L.add("smtp加密=true"); + writeLines(f, L); + } + + // ==================== 插件设置 ==================== + + public void loadSettings() { + File file = new File(dataFolder, "插件设置.txt"); + if (!file.exists()) createDefaultSettings(file); + Map m = new LinkedHashMap<>(); + loadMap(file, m); + + adminTag = m.getOrDefault("管理标签", adminTag); + adminPassword = m.getOrDefault("管理密码", adminPassword); + maxAccountsPerIP = parseInt(m.getOrDefault("每IP最大账号数", "3"), 3); + approvalMode = m.getOrDefault("审批模式", approvalMode); + autoApproveDelayMinutes = parseInt(m.getOrDefault("自动审批延迟分钟", "30"), 30); + loginTimeout = parseInt(m.getOrDefault("登录超时秒数", "60"), 60); + afkEnabled = Boolean.parseBoolean(m.getOrDefault("挂机踢出", "false")); + afkTimeout = parseInt(m.getOrDefault("挂机超时秒数", "300"), 300); + smtpSsl = Boolean.parseBoolean(m.getOrDefault("smtp加密", "true")); + httpPort = parseInt(m.getOrDefault("http-port", "8080"), 8080); + broadcastServerUrl = m.getOrDefault("resource-pack-url", ""); + + providerPointsPerScore = parseDouble(m.getOrDefault("服务商积分倍率", "2.0"), 2.0); + baseEconomyReward = parseInt(m.getOrDefault("基础经济奖励", "100"), 100); + providerEconomyPerScore = parseDouble(m.getOrDefault("经济奖励每评分", "50.0"), 50.0); + requesterPointsReward = parseInt(m.getOrDefault("报单人积分奖励", "10"), 10); + + garbageEnabled = Boolean.parseBoolean(m.getOrDefault("垃圾站_启用", "true")); + garbageInterval = parseInt(m.getOrDefault("垃圾站_清理间隔秒", "300"), 300); + garbageMaxRounds = parseInt(m.getOrDefault("垃圾站_保留轮数", "1"), 1); + + checkinRewardFixed = parseDouble(m.getOrDefault("签到固定金额", "0"), 0); + checkinRewardMin = parseDouble(m.getOrDefault("签到最小金额", "0"), 0); + checkinRewardMax = parseDouble(m.getOrDefault("签到最大金额", "0"), 0); + checkinGivePoints = Boolean.parseBoolean(m.getOrDefault("签到给积分", "true")); + checkinPoints = parseInt(m.getOrDefault("签到积分数量", "10"), 10); + backCheckPointMultiplier = parseInt(m.getOrDefault("补签积分倍率", "5"), 5); + checkinBondMin = parseInt(m.getOrDefault("签到债券最小", "1"), 1); + checkinBondMax = parseInt(m.getOrDefault("签到债券最大", "3"), 3); + if (checkinBondMax < checkinBondMin) checkinBondMax = checkinBondMin; + + if (checkinRewardFixed > 0) checkinRewardType = "fixed"; + else if (checkinRewardMin > 0 && checkinRewardMax > checkinRewardMin) checkinRewardType = "range"; + else checkinRewardType = "none"; + rewardChannel = m.getOrDefault("奖励发放方式", "债券"); + rewardChannel = normalizeRewardChannel(rewardChannel); + +// 验证合法值 + if (!"economy".equalsIgnoreCase(rewardChannel) + && !"bonds".equalsIgnoreCase(rewardChannel)) { + rewardChannel = "bonds"; + } + + + + // ★ 自动补充新配置项 ★ + boolean needAppend = false; + List newKeys = new ArrayList<>(); + if (!m.containsKey("签到债券最小")) { needAppend = true; newKeys.add("签到债券最小=" + checkinBondMin); } + if (!m.containsKey("签到债券最大")) { needAppend = true; newKeys.add("签到债券最大=" + checkinBondMax); } + if (!m.containsKey("奖励发放方式")) { + needAppend = true; + newKeys.add("奖励发放方式=债券"); + } + if (needAppend) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + for (String key : newKeys) { bw.write(key); bw.newLine(); } + bw.flush(); bw.close(); + System.out.println("[Config] 已补充签到债券配置"); + + } catch (IOException ignored) {} + if (!m.containsKey("奖励发放方式")) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + bw.write("奖励发放方式=bonds"); + bw.newLine(); + bw.flush(); bw.close(); + } catch (IOException ignored) {} + } + + } + } + /** + * 奖励方式标准化:中英文全兼容 + * "债券" "bonds" "Bond" "BONDS" → "bonds" + * "经济" "economy" "Economy" "ECONOMY" → "economy" + * 其他任何值 → "bonds"(默认债券) + */ + private String normalizeRewardChannel(String raw) { + if (raw == null) return "bonds"; + String s = raw.trim().toLowerCase(); + // 中文 + if (s.contains("债券")) return "bonds"; + if (s.contains("经济")) return "economy"; + // 英文 + if (s.contains("bond")) return "bonds"; + if (s.contains("econ")) return "economy"; + if (s.contains("Vault")) return "economy"; + // 兜底 + return "bonds"; + } + + public void saveSettings() { + File file = new File(dataFolder, "插件设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=" + adminTag); + L.add("管理密码=" + adminPassword); + L.add("每IP最大账号数=" + maxAccountsPerIP); + L.add("审批模式=" + approvalMode); + L.add("自动审批延迟分钟=" + autoApproveDelayMinutes); + L.add("登录超时秒数=" + loginTimeout); + L.add("挂机踢出=" + afkEnabled); + L.add("挂机超时秒数=" + afkTimeout); + L.add("resource-pack-url=" + broadcastServerUrl); + L.add("http-port=" + httpPort); + L.add("垃圾站_启用=" + garbageEnabled); + L.add("垃圾站_清理间隔秒=" + garbageInterval); + L.add("垃圾站_保留轮数=" + garbageMaxRounds); + L.add("签到固定金额=" + checkinRewardFixed); + L.add("签到最小金额=" + checkinRewardMin); + L.add("签到最大金额=" + checkinRewardMax); + L.add("签到给积分=" + checkinGivePoints); + L.add("签到积分数量=" + checkinPoints); + L.add("签到债券最小=" + checkinBondMin); + L.add("签到债券最大=" + checkinBondMax); + L.add("补签积分倍率=" + backCheckPointMultiplier); + L.add("奖励发放方式=债券"); + writeLines(file, L); + } + + private void createDefaultSettings(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=admin"); + L.add("管理密码=qweasd"); + L.add("每IP最大账号数=3"); + L.add("审批模式=manual"); + L.add("自动审批延迟分钟=30"); + L.add("登录超时秒数=60"); + L.add("挂机踢出=false"); + L.add("挂机超时秒数=300"); + L.add("resource-pack-url="); + L.add("http-port=8080"); + L.add("垃圾站_启用=true"); + L.add("垃圾站_清理间隔秒=300"); + L.add("垃圾站_保留轮数=1"); + L.add("签到固定金额=0"); + L.add("签到最小金额=50"); + L.add("签到最大金额=500"); + L.add("签到给积分=true"); + L.add("签到积分数量=10"); + L.add("签到债券最小=1"); + L.add("签到债券最大=3"); + L.add("补签积分倍率=5"); + L.add("奖励发放方式=债券"); + writeLines(f, L); + } + public List getAfkWhitelist() { + List list = new ArrayList<>(); + File file = new File(dataFolder, "挂机白名单.txt"); + if (!file.exists()) { + try { + file.createNewFile(); + FileWriter fw = new FileWriter(file); + fw.write("# 每行一个玩家名\n"); + fw.flush(); + fw.close(); + } catch (IOException ignored) {} + return list; + } + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(file), + StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) + list.add(line); + } + } catch (IOException ignored) {} + return list; + } + + // ==================== 其他 ==================== + + public String generateSmtpVerifyCode() { + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(); + java.util.Random r = new java.util.Random(); + for (int i = 0; i < 6; i++) sb.append(chars.charAt(r.nextInt(chars.length()))); + smtpVerifyCode = sb.toString(); + smtpVerifyExpire = System.currentTimeMillis() + 300000L; + return smtpVerifyCode; + } + + public boolean checkSmtpVerifyCode(String code) { + if (System.currentTimeMillis() > smtpVerifyExpire) { smtpVerifyCode = ""; return false; } + return smtpVerifyCode.equalsIgnoreCase(code); + } + + public void clearSmtpVerifyCode() { smtpVerifyCode = ""; smtpVerifyExpire = 0; } + public boolean canSendSmtpVerify() { return System.currentTimeMillis() - smtpLastSendTime >= 60000L; } + public void recordSmtpSendTime() { smtpLastSendTime = System.currentTimeMillis(); } + + // ==================== 文件工具 ==================== + + private void loadMap(File file, Map map) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq > 0) map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); + } + } catch (IOException ignored) {} + } + + private void writeLines(File file, List lines) { + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + for (String l : lines) { w.write(l); w.newLine(); } + } catch (IOException ignored) {} + } + + private double parseDouble(String s, double def) { try { return Double.parseDouble(s.trim()); } catch (Exception e) { return def; } } + private int parseInt(String s, int def) { try { return Integer.parseInt(s.trim()); } catch (Exception e) { return def; } } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/CypayCommand.java b/backup/20260605_152529/src/main/java/Sdf1_login/CypayCommand.java new file mode 100644 index 0000000..7fd9067 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/CypayCommand.java @@ -0,0 +1,577 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +public class CypayCommand + implements CommandExecutor, TabCompleter { + + private final Main plugin; + private static final SimpleDateFormat SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + public CypayCommand(Main plugin) { + this.plugin = plugin; + } + + private boolean hasAdminPerm(CommandSender s) { + if (s.isOp()) return true; + if (!(s instanceof Player)) return true; + Player p = (Player) s; + String tag = + plugin.getConfig2().adminTag; + return !tag.isEmpty() + && p.getScoreboardTags().contains(tag); + } + + private int parseAmount(String s) { + return Integer.parseInt( + s.trim().replaceAll("[+]", "")); + } + + private List filter( + List list, String prefix) { + String low = prefix.toLowerCase(); + return list.stream() + .filter(x -> x.toLowerCase() + .startsWith(low)) + .collect(Collectors.toList()); + } + + @Override + public boolean onCommand(CommandSender sender, + Command cmd, + String label, + String[] args) { + + // ===== /cypay — 查自己 ===== + if (args.length == 0) { + String name = sender instanceof Player + ? sender.getName() : "Console"; + int bonds = + plugin.getBonds().getBonds(name); + String status = plugin.getBonds() + .getAccountStatus(name); + sender.sendMessage( + "§e§l[债券] §f" + name + + " 余额: §a" + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals(status) + ? "§c冻结中" + : "§a正常")); + return true; + } + + // ===== /cypay import ===== + /* if (args.length == 2 + && "import".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String file = args[1]; + if (!file.endsWith(".txt")) + file += ".txt"; + int result = plugin.getCDK() + .importFromFile(file); + if (result == -1) + sender.sendMessage("§c文件不存在: " + + file); + else + sender.sendMessage("§a导入完成: " + + result + " 条"); + return true; + }*/ + + // ===== /cypay freeze ===== + if (args.length == 2 + && "freeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (plugin.getBonds().isFrozen(target)) { + sender.sendMessage("§c" + target + + " 已处于冻结状态"); + return true; + } + plugin.getBonds().freezeAccount(target); + sender.sendMessage("§a已冻结 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§c§l[系统] 你的债券账户" + + "已被管理员冻结"); + return true; + } + + // ===== /cypay unfreeze ===== + if (args.length == 2 + && "unfreeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (!plugin.getBonds() + .isFrozen(target)) { + sender.sendMessage("§c" + target + + " 未被冻结"); + return true; + } + plugin.getBonds().unfreezeAccount(target); + sender.sendMessage("§a已解冻 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a§l[系统] 你的债券账户" + + "已被解冻"); + return true; + } + + // ===== /cypay water [days] ===== + if (args.length >= 2 + && "water".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + int days = 7; + if (args.length >= 3) { + try { + days = Integer.parseInt(args[2]); + } catch (NumberFormatException ignored) { + } + } + days = Math.min(days, + BondManager.ADMIN_MAX_WATER_DAYS); + + if (days <= 0) days = 1; + + List> txs = + plugin.getBonds() + .getTransactions( + target, days); + + sender.sendMessage( + "§e§l===== " + target + + " 流水记录 (近" + + days + "天) ====="); + sender.sendMessage("§f当前余额: §a" + + plugin.getBonds() + .getBonds(target) + + " §f状态: " + + (plugin.getBonds() + .isFrozen(target) + ? "§c冻结" : "§a正常")); + + if (txs.isEmpty()) { + sender.sendMessage("§7 (暂无记录)"); + } else { + for (Map tx : txs) { + sender.sendMessage( + formatTxLine(tx)); + } + } + sender.sendMessage( + "§e§l==========================" + + "=========="); + return true; + } + // ===== /cypay printer [player] ===== + if ("printer".equalsIgnoreCase(args[0])) { + if (!hasAdminPerm(sender) + && !(sender instanceof Player) + == false) { + // 控制台也可以用 + if (!(sender instanceof Player)) + ; // 放行 + else if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + } + if (args.length == 1) { + // 打印全服 + sender.sendMessage( + "§e正在导出全服流水..."); + plugin.getBonds(); // 确保实例就绪 + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printAll(sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + if (args.length == 2) { + // 打印指定玩家 + String target = args[1]; + sender.sendMessage("§e正在导出 " + + target + " 的流水..."); + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printPlayer(target, sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + } + + // ===== /cypay give/remove ===== + if (args.length == 3) { + String action = args[0].toLowerCase(); + if ("give".equals(action) + || "remove".equals(action)) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + try { + int amount = + parseAmount(args[2]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + String txType = + "give".equals(action) + ? BondManager + .TX_ADMIN_GIVE + : BondManager + .TX_ADMIN_DEDUCT; + String opName = + sender instanceof Player + ? sender.getName() + : "Console"; + + if ("give".equals(action)) { + plugin.getBonds().addBonds( + target, amount, + txType, "", + opName, "管理员发放"); + sender.sendMessage( + "§a已给 " + target + + " " + amount + + " 债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a管理员给了你 " + + amount + + " 债券"); + } else { + if (plugin.getBonds() + .deductBonds(target, + amount, + txType, "", + opName, + "管理员扣除")) { + sender.sendMessage( + "§a已扣除 " + target + + " " + amount + + " 债券"); + Player tp = plugin + .getServer() + .getPlayerExact( + target); + if (tp != null) + tp.sendMessage( + "§c管理员扣除了" + + "你的 " + + amount + + " 债券"); + } else { + sender.sendMessage( + "§c" + target + + " 债券不足" + + "或账户冻结"); + } + } + } catch (NumberFormatException e) { + sender.sendMessage( + "§c金额格式错误"); + } + return true; + } + } + + // ===== /cypay — 仅玩家转账 ===== + if (args.length == 2) { + try { + int amount = parseAmount(args[1]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + // ★ 控制台禁止转账 + if (!(sender instanceof Player)) { + sender.sendMessage( + "§c控制台不能转账,请使用 " + + "/cypay give <玩家> <金额>"); + return true; + } + Player p = (Player) sender; + String target = args[0]; + BondManager.TransferResult r = + plugin.getBonds().transfer( + p.getName(), + target, amount); + if (r.success) { + p.sendMessage("§e§l[债券转账] §f向 §a" + + target + " §f转出 §c" + + amount + " §f枚债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage("§e§l[债券转账] §f收到 §a" + + p.getName() + + " §f转入 §a" + + amount + " §f枚债券"); + } else { + p.sendMessage("§c" + r.message + + ",当前: " + + plugin.getBonds() + .getBonds(p.getName())); + } + return true; + } catch (NumberFormatException ignored) { + } + } + + // ===== /cypay — 查指定玩家 ===== + if (args.length == 1) { + String target = args[0]; + // ★ 验证玩家在 login.db 中存在 ★ + if (!plugin.getDb().userExists(target)) { + sender.sendMessage("§c玩家 " + target + + " 不存在于服务器"); + return true; + } + + // String target = args[0]; + if (hasAdminPerm(sender)) { + int bonds = plugin.getBonds() + .getBonds(target); + String status = plugin.getBonds() + .getAccountStatus(target); + sender.sendMessage( + "§e§l[债券] §f" + target + + " 余额: §a" + + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals( + status) + ? "§c冻结中" + : "§a正常")); + } else { + int bonds = plugin.getBonds() + .getBonds( + sender.getName()); + sender.sendMessage( + "§e§l[债券] §f你的余额: §a" + + bonds); + } + return true; + } + + showHelp(sender); + return true; + } + + // ===== 流水行格式化 ===== + + private String formatTxLine( + Map tx) { + String type = (String) tx.get("type"); + int amount = ((Number) tx.get("amount")) + .intValue(); + String tgt = (String) tx.get("target_player"); + String reason = (String) tx.get("reason"); + int bal = ((Number) tx.get("balance_after")) + .intValue(); + long time = ((Number) tx.get("time")) + .longValue(); + + boolean pos = amount > 0; + String sign = pos ? "+" : ""; + String color = pos ? "§a" : "§c"; + + StringBuilder sb = new StringBuilder(); + sb.append("§7[") + .append(SDF.format(new Date(time))) + .append("] ") + .append(formatTxType(type)) + .append(" ") + .append(color).append(sign) + .append(amount).append("§7"); + if (tgt != null && !tgt.isEmpty()) + sb.append(" §e↔ ").append(tgt); + if (reason != null && !reason.isEmpty()) + sb.append(" §7(").append(reason) + .append(")"); + sb.append(" §7余额:§f").append(bal); + return sb.toString(); + } + + private String formatTxType(String type) { + if (type == null) return "§7未知"; + switch (type) { + case BondManager.TX_ADMIN_GIVE: + return "§a管理员发放"; + case BondManager.TX_ADMIN_DEDUCT: + return "§c管理员扣除"; + case BondManager.TX_TRANSFER_OUT: + return "§c转出"; + case BondManager.TX_TRANSFER_IN: + return "§a转入"; + case BondManager.TX_REDEEM: + return "§aCDK兑换"; + case BondManager.TX_SHOP_BUY: + return "§e商城购买"; + case BondManager.TX_DAILY_SIGN: + return "§a签到奖励"; + case BondManager.TX_FREEZE: + return "§c冻结"; + case BondManager.TX_UNFREEZE: + return "§a解冻"; + default: + return "§7" + type; + } + } + + private void showHelp(CommandSender sender) { + sender.sendMessage("§e§l债券命令帮助:"); + sender.sendMessage( + "§e/cypay §7- 查询自己余额"); + sender.sendMessage( + "§e/cypay <玩家> §7- 查余额(管理员)"); + sender.sendMessage( + "§e/cypay <玩家> <金额> §7- 转账"); + if (hasAdminPerm(sender)) { + sender.sendMessage( + "§e/cypay give <玩家> <金额> §7- 给债券"); + sender.sendMessage( + "§e/cypay remove <玩家> <金额> §7- 扣债券"); + sender.sendMessage( + "§e/cypay import <文件> §7- 导入CDK"); + sender.sendMessage( + "§e/cypay freeze <玩家> §7- 冻结"); + sender.sendMessage( + "§e/cypay unfreeze <玩家> §7- 解冻"); + sender.sendMessage( + "§e/cypay water <玩家> [天数] §7- 流水"); + sender.sendMessage( + "§e/cypay printer §7- 导出全服流水xlsx"); + sender.sendMessage( + "§e/cypay printer <玩家> §7- 导出指定玩家xlsx"); + + } + } + + @Override + public List onTabComplete( + CommandSender sender, Command command, + String label, String[] args) { + List list = new ArrayList<>(); + + if (args.length == 1) { + String last = args[0].toLowerCase(); + if (hasAdminPerm(sender)) { + list.add("give"); + list.add("remove"); + list.add("import"); + list.add("freeze"); + list.add("unfreeze"); + list.add("water"); + list.add("printer"); + + File cdkDir = new File( + plugin.getDataFolder(), + "cdk"); + if (cdkDir.exists()) { + File[] files = cdkDir.listFiles( + (d, n) -> n + .endsWith(".txt")); + if (files != null) + for (File f : files) + list.add(f.getName()); + } + } + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, last); + } + + if (args.length == 2) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first) + || "freeze".equals(first) + || "unfreeze".equals(first) + || "water".equals(first)) { + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, + args[1].toLowerCase()); + } + list.add("10"); + list.add("50"); + list.add("100"); + return filter(list, + args[1].toLowerCase()); + } + + if (args.length == 3) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first)) { + list.add("10"); + list.add("50"); + list.add("100"); + list.add("999"); + return filter(list, + args[2].toLowerCase()); + } + if ("water".equals(first)) { + list.add("1"); + list.add("3"); + list.add("7"); + list.add("14"); + return filter(list, + args[2].toLowerCase()); + } + } + + return list; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/DatabaseManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/DatabaseManager.java new file mode 100644 index 0000000..cb2b8f7 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/DatabaseManager.java @@ -0,0 +1,1758 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.logging.Logger; + +public class DatabaseManager { + + private Connection db; + private final File dataFolder; + private final Logger logger; + + public DatabaseManager(File dataFolder) { + this.dataFolder = dataFolder; + this.logger = Logger.getLogger("Sdf1_login"); + } + + // ==================== 初始化 ==================== + + public void init() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(dataFolder, + "login.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 用户表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "users (" + + "player_name TEXT PRIMARY KEY," + + "password_hash TEXT NOT NULL," + + "password_salt TEXT NOT NULL," + + "email TEXT DEFAULT ''," + + "register_time INTEGER DEFAULT 0," + + "last_login_time INTEGER DEFAULT 0," + + "total_online_time INTEGER DEFAULT 0," + + "last_online_check INTEGER DEFAULT 0," + + "is_logged_in INTEGER DEFAULT 0," + + "blocks_broken INTEGER DEFAULT 0," + + "blocks_placed INTEGER DEFAULT 0," + + "gift_stage INTEGER DEFAULT 0," + + "gift_claimed TEXT DEFAULT ''," + + "invite_code TEXT DEFAULT ''," + + "invited_by TEXT DEFAULT ''," + + "monthly_invite_count INTEGER DEFAULT 0," + + "invite_month TEXT DEFAULT ''," + + "points INTEGER DEFAULT 0," + + "checkin_streak INTEGER DEFAULT 0," + + "last_checkin_date TEXT DEFAULT ''," + + "total_checkin_days INTEGER DEFAULT 0," + + "temp_password TEXT DEFAULT ''," + + "temp_pw_expire INTEGER DEFAULT 0," + + "temp_pw_used INTEGER DEFAULT 0," + + "ip_address TEXT DEFAULT ''," + + "tasks_completed TEXT DEFAULT ''," + + "premium_uuid TEXT DEFAULT ''," + + "register_ip TEXT DEFAULT ''," + + "register_type TEXT DEFAULT 'manual'," + + "backcheck_dates TEXT DEFAULT ''" + + ")"); + + + // 安全添加 users 缺失列 + safeAdd(st, "tasks_completed", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_password", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_pw_expire", + "INTEGER DEFAULT 0"); + safeAdd(st, "temp_pw_used", + "INTEGER DEFAULT 0"); + safeAdd(st, "ip_address", + "TEXT DEFAULT ''"); + safeAdd(st, "premium_uuid", + "TEXT DEFAULT ''"); + safeAdd(st, "register_ip", + "TEXT DEFAULT ''"); + safeAdd(st, "register_type", + "TEXT DEFAULT 'manual'"); + + // ===== 背包备份表:安全迁移 ===== + // 检查旧表是否存在(用 inventory_data 列判断) + boolean hasOldTable = false; + try { + ResultSet rs = st.executeQuery( + "SELECT inventory_data " + + "FROM inventory_backups " + + "LIMIT 1"); + hasOldTable = true; + rs.close(); + } catch (SQLException e) { + hasOldTable = false; + } + + if (hasOldTable) { + // 旧表存在,迁移数据到新表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups_new (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + // 迁移旧数据 + try { + st.execute("INSERT INTO " + + "inventory_backups_new " + + "(player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience) " + + "SELECT player_name, " + + "inventory_data, " + + "armor_data, " + + "extra_data, " + + "player_level, " + + "player_exp " + + "FROM inventory_backups"); + } catch (SQLException ignored) { + } + st.execute("DROP TABLE " + + "inventory_backups"); + st.execute("ALTER TABLE " + + "inventory_backups_new " + + "RENAME TO " + + "inventory_backups"); + } else { + // 无旧表或已迁移,直接创建 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + } + + // 安全报警表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "security_alerts (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "alert_ip TEXT NOT NULL," + + "alert_time INTEGER NOT NULL," + + "alert_date TEXT NOT NULL," + + "notified INTEGER DEFAULT 0," + + "notified_time INTEGER DEFAULT 0)"); + // PVP区域表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "name TEXT NOT NULL UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER DEFAULT 0" + + ")"); + +// PVP击杀总榜表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT NOT NULL," + + "region_name TEXT NOT NULL," + + "kills INTEGER DEFAULT 0," + + "deaths INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, region_name)" + + ")"); + + +// 菜单图标表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "menu_icons (" + + "player_name TEXT PRIMARY KEY," + + "icon_b64 TEXT DEFAULT ''," + + "item_name TEXT DEFAULT ''," + + "save_time INTEGER DEFAULT 0" + + ")"); + + // 工单表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "tickets (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "type TEXT NOT NULL," + + "status TEXT DEFAULT 'pending'," + + "priority TEXT DEFAULT 'normal'," + + "requester TEXT NOT NULL," + + "assigned_to TEXT DEFAULT ''," + + "title TEXT DEFAULT ''," + + "description TEXT DEFAULT ''," + + "reward_amount INTEGER DEFAULT 0," + + "score INTEGER DEFAULT 0," + + "target_player TEXT DEFAULT ''," + + "create_time INTEGER NOT NULL," + + "update_time INTEGER DEFAULT 0," + + "complete_time INTEGER DEFAULT 0," + + "admin_confirmed INTEGER DEFAULT 0," + + "reject_reason TEXT DEFAULT ''" + + ")"); + + // 工单回复表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "ticket_replies (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "ticket_id INTEGER NOT NULL," + + "sender TEXT NOT NULL," + + "message TEXT NOT NULL," + + "create_time INTEGER NOT NULL" + + ")"); + + // 服务商表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "service_providers (" + + "player_name TEXT PRIMARY KEY," + + "role TEXT DEFAULT 'waiter'," + + "active INTEGER DEFAULT 1," + + "join_time INTEGER DEFAULT 0" + + ")"); + // 垃圾回收站物品表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "items (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "item_data TEXT DEFAULT ''," + + "amount INTEGER DEFAULT 1," + + "create_time INTEGER DEFAULT 0" + + ")"); + safeAdd(st, "menu_snowball", + "INTEGER DEFAULT 1"); + st.close(); + logger.info("[Sdf1_login] 数据库初始化完成"); + } catch (Exception e) { + throw new RuntimeException( + "[Sdf1_login] DB初始化失败: " + + e.getMessage(), e); + } + } + + + private void safeAdd(Statement st, + String col, String def) { + try { + st.executeUpdate("ALTER TABLE users " + + "ADD COLUMN " + col + " " + def); + } catch (SQLException ignored) { + } + } + + public void close() { + try { + if (db != null && !db.isClosed()) + db.close(); + } catch (Exception ignored) { + } + } +// ==================== 菜单图标 ==================== + + public void saveMenuIcon(String name, + String iconB64, String itemName) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO menu_icons " + + "(player_name, icon_b64, " + + "item_name, save_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, iconB64); + ps.setString(3, itemName); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT icon_b64 FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + String b64 = null; + if (rs.next()) { + b64 = rs.getString("icon_b64"); + } + rs.close(); + ps.close(); + return b64; + } catch (SQLException e) { + return null; + } + } + + public void deleteMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM menu_icons " + + "WHERE player_name=? " + + "AND icon_b64 IS NOT NULL " + + "AND icon_b64 != '' " + + "LIMIT 1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + // ==================== 用户操作 ==================== + + public boolean userExists(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public void createUser(String name, String hash, + String salt) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO users " + + "(player_name, " + + "password_hash, " + + "password_salt, " + + "register_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, hash); + ps.setString(3, salt); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ========== 临时密码管理 ========== + + public void clearTempPassword(String name) { + setField(name, "temp_password", null); + setField(name, "temp_pw_expire", 0); + setField(name, "temp_pw_used", 1); + } + + public void clearAllTempPasswords() { + try { + Statement st = db.createStatement(); + st.executeUpdate( + "UPDATE users SET " + + "temp_password=NULL," + + "temp_pw_expire=0," + + "temp_pw_used=1 " + + "WHERE temp_password " + + "IS NOT NULL"); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPlayerIPByName( + String name) { + Object ip = getField(name, "ip"); + return ip != null + ? String.valueOf(ip) : null; + } + + public boolean checkPasswordOrTemp( + String name, String hash) { + // 先检查主密码 + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String mainHash = + rs.getString("password_hash"); + rs.close(); + ps.close(); + if (hash.equals(mainHash)) + return true; + } else { + rs.close(); + ps.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + // 再检查临时密码 + try { + PreparedStatement ps2 = db.prepareStatement( + "SELECT temp_password, " + + "temp_pw_expire, " + + "temp_pw_used " + + "FROM users " + + "WHERE player_name=?"); + ps2.setString(1, name); + ResultSet rs2 = ps2.executeQuery(); + if (rs2.next()) { + String tempHash = + rs2.getString( + "temp_password"); + long expire = + rs2.getLong( + "temp_pw_expire"); + int used = + rs2.getInt( + "temp_pw_used"); + rs2.close(); + ps2.close(); + if (tempHash != null + && !tempHash.isEmpty() + && hash.equals(tempHash) + && System.currentTimeMillis() + < expire + && used != 1) { + return true; + } + } else { + rs2.close(); + ps2.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + public Map getUser(String name) { + Map r = + new LinkedHashMap<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + r.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return r; + } + + public void setField(String name, String field, + Object val) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE users SET " + field + + "=? " + + "WHERE player_name=?"); + if (val instanceof Integer) + ps.setInt(1, (Integer) val); + else if (val instanceof Long) + ps.setLong(1, (Long) val); + else + ps.setString(1, + String.valueOf(val)); + ps.setString(2, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Object getField(String name, + String field) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT " + field + + " FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + Object val = rs.next() + ? rs.getObject(1) : null; + rs.close(); + ps.close(); + return val; + } catch (SQLException e) { + return null; + } + } + + public boolean checkPassword(String name, + String hash) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String stored = + rs.getString("password_hash"); + rs.close(); + ps.close(); + return hash.equals(stored); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + + public boolean isUsingTempPassword(String name) { + String h = (String) getField(name, + "temp_password"); + return h != null && !h.isEmpty(); + } + + public void setLoggedIn(String name, + boolean val) { + setField(name, "is_logged_in", + val ? 1 : 0); + } + + public void addPoints(String name, int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + setField(name, "points", curVal + amount); + } + + public boolean deductPoints(String name, + int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + if (curVal < amount) return false; + setField(name, "points", + curVal - amount); + return true; + } + public List> + getAllUsers() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM users"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + public List> + getTicketsByProviderAll( + String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deleteUser(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void recordIP(String name, String ip) { + if (ip != null && !ip.isEmpty()) { + setField(name, "ip_address", ip); + } + } + + // ==================== IP查询 ==================== + + /** + * 统计指定IP注册的账号数量 + */ + public int countIPAccounts(String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address=?"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count; + } catch (SQLException e) { + return 0; + } + } + + /** + * 获取指定IP下所有账号详情 + */ + public List> + getAccountDetailsByIP(String ip) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, " + + "register_time " + + "FROM users " + + "WHERE ip_address=? " + + "ORDER BY register_time"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("register_time", + rs.getLong("register_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 背包备份 ==================== + + public void saveInventoryBackup(String name, + String contents, String armor, + String extra, int level, + double experience) { + try { + PreparedStatement del = + db.prepareStatement( + "DELETE FROM " + + "inventory_backups " + + "WHERE player_name=?"); + del.setString(1, name); + del.executeUpdate(); + del.close(); + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO " + + "inventory_backups" + + " (player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience, " + + "save_time) " + + "VALUES " + + "(?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, contents); + ps.setString(3, armor); + ps.setString(4, extra); + ps.setInt(5, level); + ps.setDouble(6, experience); + ps.setLong(7, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public String[] loadInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time " + + "DESC LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void deleteInventoryBackup( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM inventory_backups " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + boolean has = rs.next(); + rs.close(); + ps.close(); + return has; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +// ==================== PVP区域 ==================== + + public void savePVPRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions " + + "(name, world, " + + "x1, y1, z1, " + + "x2, y2, z2, " + + "create_time) " + + "VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map + getPVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getAllPVPRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + db.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deletePVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +// ==================== PVP击杀 ==================== + + public int getPVPKills( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int kills = 0; + if (rs.next()) + kills = rs.getInt("kills"); + rs.close(); + ps.close(); + return kills; + } catch (SQLException e) { + return 0; + } + } + + public int getPVPDeaths( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int deaths = 0; + if (rs.next()) + deaths = rs.getInt("deaths"); + rs.close(); + ps.close(); + return deaths; + } catch (SQLException e) { + return 0; + } + } + + public void addPVPKill( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,1,0) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "kills=kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void addPVPDeath( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,0,1) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "deaths=deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getPVPKillTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT player_name, " + + "kills, deaths " + + "FROM pvp_kills " + + "WHERE " + + "region_name=? " + + "AND kills>0 " + + "ORDER BY kills " + + "DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + // ==================== 安全报警 ==================== + + public void recordSecurityAlert( + String playerName, String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO security_alerts " + + "(player_name, alert_ip, " + + "alert_time, alert_date) " + + "VALUES (?,?,?,?)"); + ps.setString(1, playerName); + ps.setString(2, ip); + long now = System.currentTimeMillis(); + ps.setLong(3, now); + ps.setString(4, + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date( + now))); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + public List> + getUnnotifiedAlerts( + String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM security_alerts " + + "WHERE player_name=? " + + "AND notified=0 " + + "ORDER BY alert_time ASC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void markAlertNotified(int alertId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE security_alerts " + + "SET notified=1, " + + "notified_time=? " + + "WHERE id=?"); + ps.setLong(1, + System.currentTimeMillis()); + ps.setInt(2, alertId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + /** + * 获取玩家最近N条背包备份 + */ + public List> + getInventoryBackups(String name, + int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id, save_time " + + "FROM inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time DESC " + + "LIMIT ?"); + ps.setString(1, name); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", + rs.getInt("id")); + row.put("save_time", + rs.getLong("save_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + /** + * 获取指定ID的备份数据 + */ + public String[] getInventoryBackupById( + int backupId) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM inventory_backups " + + "WHERE id=?"); + ps.setInt(1, backupId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + + // ==================== 工单操作 ==================== + + public int createTicket(String type, + String status, String priority, + String requester, String title, + String description, + int rewardAmount, + String targetPlayer) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO tickets " + + "(type, status, priority, " + + "requester, title, " + + "description, " + + "reward_amount, " + + "target_player, " + + "create_time) " + + "VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, type); + ps.setString(2, status); + ps.setString(3, priority); + ps.setString(4, requester); + ps.setString(5, title); + ps.setString(6, description); + ps.setInt(7, rewardAmount); + ps.setString(8, targetPlayer); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ResultSet rs = + ps.getGeneratedKeys(); + int id = -1; + if (rs.next()) id = rs.getInt(1); + rs.close(); + ps.close(); + return id; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + public void updateTicketStatus(int ticketId, + String status) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET status=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, status); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void assignTicket(int ticketId, + String provider) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "assigned_to=?, " + + "status='processing', " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, provider); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void completeTicket(int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='completed', " + + "complete_time=?, " + + "update_time=? " + + "WHERE id=?"); + long now = System.currentTimeMillis(); + ps.setLong(1, now); + ps.setLong(2, now); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void confirmTicket(int ticketId, + int score) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "admin_confirmed=1, " + + "score=?, " + + "status='resolved', " + + "update_time=? " + + "WHERE id=?"); + ps.setInt(1, score); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public void rejectTicketComplete(int ticketId, + String reason) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='rejected', " + + "reject_reason=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, reason); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 更新工单任意字段 + */ + public void updateTicketField(int ticketId, + String field, Object value) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + field + + "=? WHERE id=?"); + if (value instanceof Integer) + ps.setInt(1, (Integer) value); + else if (value instanceof Long) + ps.setLong(1, (Long) value); + else + ps.setString(1, + String.valueOf(value)); + ps.setInt(2, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map getTicket( + int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE id=?"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getTicketsByStatus(String status) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status=? " + + "ORDER BY create_time DESC"); + ps.setString(1, status); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void addTicketReply(int ticketId, + String sender, String message) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO ticket_replies " + + "(ticket_id, sender, " + + "message, create_time) " + + "VALUES (?,?,?,?)"); + ps.setInt(1, ticketId); + ps.setString(2, sender); + ps.setString(3, message); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + // 更新工单状态为已回复 + updateTicketStatus(ticketId, + "replied"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getTicketReplies(int ticketId) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM ticket_replies " + + "WHERE ticket_id=? " + + "ORDER BY create_time ASC"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + /** + * 获取已回复但超时未互动的工单 + */ + public List> + getRepliedStaleTickets( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='replied' " + + "AND update_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByPlayer(String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE requester=? " + + "ORDER BY create_time DESC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByProvider(String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "AND (status='processing' " + + "OR status='replied') " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getUnconfirmedCompleted( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='completed' " + + "AND admin_confirmed=0 " + + "AND complete_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 服务商操作 ==================== + + public void addServiceProvider(String name, + String role) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO " + + "service_providers " + + "(player_name, role, " + + "active, join_time) " + + "VALUES (?,?,1,?)"); + ps.setString(1, name); + ps.setString(2, role); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void removeServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM service_providers " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean isServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM service_providers " + + "WHERE player_name=? " + + "AND active=1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public List> + getAllServiceProviders() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM service_providers " + + "WHERE active=1"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Connection getDb() { + return db; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/EmailManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/EmailManager.java new file mode 100644 index 0000000..b6906e5 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/EmailManager.java @@ -0,0 +1,284 @@ +package Sdf1_login; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Base64; +import java.util.logging.Logger; + +public class EmailManager { + + private final ConfigManager config; + private final Logger logger; + + public EmailManager(ConfigManager config) { + this.config = config; + this.logger = Logger.getLogger("Sdf1_login"); + } + + public boolean isConfigured() { + String host = config.getSmtp("smtp地址"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + return host != null && !host.isEmpty() + && user != null && !user.isEmpty() + && pass != null && !pass.isEmpty(); + } + + public boolean sendVerifyCode(String to, + String code) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置," + + "无法发送验证码"); + return false; + } + logger.info("[Sdf1_login] 发送验证码到 " + to); + return sendEmailSync(to, + "[Sdf1_login] 验证码", + "您的验证码: " + code + + "\n5分钟内有效。\n" + + "如非本人操作请忽略。"); + } + + public boolean sendTempPassword(String to, + String player, String tempPwd) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, + "[Sdf1_login] 临时密码", + "玩家 " + player + + " 的临时密码: " + tempPwd + + "\n请尽快登录并修改密码!"); + } + + public boolean sendBody(String to, String subject, String body) { + if (!isConfigured()) { + logger.warning("[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, subject, body); + } + + + private boolean sendEmailSync(String to, + String subject, String body) { + final boolean[] result = {false}; + Thread t = new Thread(() -> { + try { + result[0] = sendEmail( + to, subject, body); + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件线程异常: " + + e.getMessage()); + } + }, "Sdf1_login-SMTP"); + t.setDaemon(true); + t.start(); + try { + t.join(15000); + } catch (InterruptedException ignored) { + } + if (t.isAlive()) { + t.interrupt(); + logger.severe( + "[Sdf1_login] SMTP超时(15秒)"); + return false; + } + return result[0]; + } + + private boolean sendEmail(String to, + String subject, String body) { + String host = config.getSmtp("smtp地址"); + String portStr = config.getSmtp("smtp端口"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + String fromName = config.getSmtp( + "发件人名称"); + + int port; + try { + port = Integer.parseInt(portStr); + } catch (Exception e) { + port = 465; + } + + try { + String encSubject = "=?UTF-8?B?" + + Base64.getEncoder() + .encodeToString( + subject.getBytes("UTF-8")) + + "?="; + String encBody = Base64.getEncoder() + .encodeToString( + body.getBytes("UTF-8")); + + if (port == 465) { + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket socket = (SSLSocket) + factory.createSocket( + host, port); + socket.setSoTimeout(10000); + doSmtp(socket, host, user, pass, + to, encSubject, encBody, + fromName); + socket.close(); + } else { + Socket plainSocket = + new Socket(host, port); + plainSocket.setSoTimeout(10000); + BufferedReader preReader = + new BufferedReader( + new InputStreamReader( + plainSocket + .getInputStream(), + "UTF-8")); + PrintWriter preWriter = + new PrintWriter( + plainSocket + .getOutputStream(), + true); + checkResponse(preReader, "220"); + sendLine(preWriter, + "EHLO " + host); + checkResponse(preReader, "250"); + sendLine(preWriter, "STARTTLS"); + checkResponse(preReader, "220"); + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket sslSocket = (SSLSocket) + factory.createSocket( + plainSocket, host, + port, true); + sslSocket.startHandshake(); + doSmtp(sslSocket, host, user, pass, + to, encSubject, encBody, + fromName); + sslSocket.close(); + } + logger.info( + "[Sdf1_login] 邮件发送成功 -> " + + to); + return true; + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件异常: " + + e.getClass() + .getSimpleName() + + ": " + e.getMessage()); + if (e.getCause() != null) { + logger.severe( + "[Sdf1_login] 根因: " + + e.getCause() + .getMessage()); + } + return false; + } + } + + private void doSmtp(SSLSocket socket, + String host, String user, String pass, + String to, String subject, String body, + String fromName) throws Exception { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + socket.getInputStream(), + "UTF-8")); + OutputStream rawOut = + socket.getOutputStream(); + PrintWriter writer = + new PrintWriter( + new java.io.BufferedOutputStream( + rawOut), true); + + checkResponse(reader, "220"); + sendLine(writer, "EHLO " + host); + checkResponse(reader, "250"); + sendLine(writer, "AUTH LOGIN"); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + user.getBytes("UTF-8"))); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + pass.getBytes("UTF-8"))); + checkResponse(reader, "235"); + sendLine(writer, + "MAIL FROM:<" + user + ">"); + checkResponse(reader, "250"); + sendLine(writer, + "RCPT TO:<" + to + ">"); + checkResponse(reader, "250"); + sendLine(writer, "DATA"); + checkResponse(reader, "354"); + sendLine(writer, + "From: " + fromName + + " <" + user + ">"); + sendLine(writer, + "To: <" + to + ">"); + sendLine(writer, + "Reply-To: <" + user + ">"); + sendLine(writer, + "Subject: " + subject); + sendLine(writer, "MIME-Version: 1.0"); + sendLine(writer, + "Content-Type: text/plain; " + + "charset=UTF-8"); + sendLine(writer, + "Content-Transfer-Encoding: " + + "base64"); + sendLine(writer, ""); + sendLine(writer, body); + sendLine(writer, "."); + checkResponse(reader, "250"); + sendLine(writer, "QUIT"); + } + + private void sendLine(PrintWriter writer, + String line) { + writer.print(line + "\r\n"); + writer.flush(); + } + + private void checkResponse( + BufferedReader reader, + String expected) throws Exception { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + if (line.length() >= 4 + && Character.isDigit( + line.charAt(0)) + && line.charAt(3) == ' ') + break; + } + String resp = sb.toString().trim(); + if (!resp.startsWith(expected)) { + if (resp.startsWith("535")) + logger.severe( + "[Sdf1_login] SMTP 535 " + + "认证失败"); + throw new Exception( + "SMTP响应异常: 期望 " + + expected + + ",实际: " + resp); + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/GUIManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/GUIManager.java new file mode 100644 index 0000000..cbc7102 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/GUIManager.java @@ -0,0 +1,2681 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + + + + +import java.util.*; + +public class GUIManager implements Listener { + + private final Main plugin; + + public static final String T_MAIN = + "§6§l服务器菜单"; + public static final String T_MORE = + "§6§l更多功能"; + public static final String T_EDITOR = + "§6§l编辑菜单项"; + public static final String T_BIOME = + "§b§l目标群系传送"; + public static final String T_MY_INFO = + "§6§l我的信息"; + public static final String T_GIFT_STAGES = + "§6§l礼包阶段"; + public static final String T_ADMIN = + "§6§l管理员面板"; + public static final String T_INVITE = + "§6§l邀请数据"; + public static final String T_USER_MGMT = + "§6§l用户管理"; + public static final String T_TASK_CENTER = + "§6§l任务中心"; + private final Map userMgmtPages = new HashMap<>(); + private static final int UMGMT_PAGE_SIZE = 45; + + + + public GUIManager(Main plugin) { + this.plugin = plugin; + } + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_MAIN); +// ===== 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + + // 已占用的槽位 + Set occupied = new HashSet<>(); + occupied.add(10); + occupied.add(12); + occupied.add(14); + occupied.add(16); + occupied.add(19); + occupied.add(21); + occupied.add(23); + occupied.add(25); + occupied.add(29); + occupied.add(31); + occupied.add(32); + occupied.add(34); + + // 自定义区域:第1排(0-8) + 第5排(36-44) + 第6排(45-53) + int[] customArea = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53 + }; + + int autoIdx = 0; + for (int ci = 0; + ci < customs.size() && ci < 27; ci++) { + MenuManager.MenuItem mi = + customs.get(ci); + + // 权限过滤 + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + + int targetSlot = -1; + + // 玩家指定了格子? + if (mi.slot != null) { + targetSlot = findBestSlot( + mi.slot - 1, occupied); + } + + // 未指定或冲突 → 自动排队 + if (targetSlot < 0) { + while (autoIdx < customArea.length) { + int s = customArea[autoIdx++]; + if (!occupied.contains(s)) { + targetSlot = s; + break; + } + } + } + + if (targetSlot < 0 + || targetSlot >= 54) continue; + occupied.add(targetSlot); + + // 放置物品 + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(java.util.Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(targetSlot, icon); + } + } + +// 背景填充 + fillBg(g); + p.openInventory(g); + + // ===== Row1(9-17): 核心功能 ===== + g.setItem(10, mkItem(Material.PLAYER_HEAD, + "§a§l我的信息")); + g.setItem(12, mkItem(Material.PAPER, + "§b§l邀请数据")); + g.setItem(14, mkItem(Material.EMERALD, + "§d§l积分商城")); + g.setItem(16, mkItem(Material.COMPASS, + "§b§l群系传送", + "§7传送到指定群系")); + + // ===== Row2(18-26): 日常功能 ===== + g.setItem(19, mkItem(Material.BOOK, + "§6工单系统")); + g.setItem(21, mkItem(Material.EMERALD_BLOCK, + "§a§l每日签到", + "§7签到获取积分和惊喜奖励")); + g.setItem(23, mkItem(Material.ENDER_CHEST, + "§6§l任务中心")); + if (p.isOp() || isAdmin(p)) { + g.setItem(25, mkItem(Material.HOPPER, + "§6§l垃圾回收站", + "§7查看和取回清理的物品")); + + + } + // ===== Row3(27-35): 管理功能 ===== + // ★ 商店 - 所有人都能看 ★ + g.setItem(27, mkItem(Material.CHEST, + "§6§l商店", + "§7浏览、购买、出售物品", + "§7货币: 债券")); + + if (isAdmin(p)) { + g.setItem(29, mkItem( + Material.REDSTONE_BLOCK, + "§c§l管理员面板")); + } + if (p.isOp()) { + g.setItem(31, mkItem(Material.ECHO_SHARD, + "§c§l管理菜单", + "§7添加、编辑、删除菜单项")); + } + // 在 openMain 方法中,Row3 区域加: + + // ★ 余额查询 - 所有人都能看 ★ + double myBal = 0; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault.economy + .Economy.class); + if (reg != null) + myBal = reg.getProvider() + .getBalance(p); + } catch (Exception ignored) {} + int myBonds = plugin.getBonds().getBonds(p.getName()); + g.setItem(32, mkItem(Material.EMERALD, + "§b§l我的钱包", + "§7查看债券余额与流水记录")); + +// ★ 余额操作 - 仅OP和tag玩家 ★ + if (isAdmin(p)) { + g.setItem(34, mkItem(Material.GOLD_INGOT, + "§6§l余额操作", + "§7给钱、扣钱")); + } + + + + // ===== Row0(0-8): 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + for (int i = 0; + i < customs.size() && i < 9; + i++) { + MenuManager.MenuItem mi = + customs.get(i); + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(i, icon); + } + } + + // 背景最后填充(不覆盖物品) + fillBg(g); + p.openInventory(g); + } + private int findBestSlot( + int requested, + Set occupied) { + // 目标位空闲直接用 + if (requested >= 0 && requested < 54 + && !occupied.contains(requested)) { + return requested; + } + // BFS上下左右找最近空位 + int row = requested / 9; + int col = requested % 9; + boolean[][] visited = + new boolean[6][9]; + java.util.Queue queue = + new java.util.LinkedList<>(); + queue.add(new int[]{row, col}); + visited[row][col] = true; + int[][] dirs = { + {-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + while (!queue.isEmpty()) { + int[] cur = queue.poll(); + for (int[] d : dirs) { + int nr = cur[0] + d[0]; + int nc = cur[1] + d[1]; + if (nr >= 0 && nr < 6 + && nc >= 0 && nc < 9 + && !visited[nr][nc]) { + visited[nr][nc] = true; + int s = nr * 9 + nc; + if (!occupied.contains(s)) { + return s; + } + queue.add(new int[]{nr, nc}); + } + } + } + return -1; + } + + public void openMenuManager(Player p) { + List list = + plugin.getMenu().getItems(); + Inventory g = Bukkit.createInventory( + null, 27, + "§c§l菜单管理"); + fillBg(g); + + for (int i = 0; + i < list.size() && i < 9; i++) { + MenuManager.MenuItem mi = list.get(i); + g.setItem(i, mkItem( + mi.icon.getType(), + "§e" + mi.title, + "§7指令: " + mi.command, + "§7权限: " + mi.permType, + "", + "§a左键编辑", + "§7右键切换权限")); + } + + g.setItem(22, mkItem(Material.LIME_WOOL, + "§a§l添加菜单项")); + + g.setItem(26, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + + public void openBalanceOps(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§d§l余额操作"); + fillBg(g); + + net.milkbowl.vault.economy.Economy eco = null; + try { + var reg = plugin.getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); + if (reg != null) eco = reg.getProvider(); + } catch (Exception ignored) {} + + int slot = 0; + for (Player op : Bukkit.getOnlinePlayers()) { + if (slot >= 45) break; + double bal = 0; + if (eco != null) bal = eco.getBalance(op); + boolean isSelf = op.getName().equals(p.getName()); + int bondBal = plugin.getBonds().getBonds(op.getName()); + g.setItem(slot, mkItem( + isSelf ? Material.EMERALD_BLOCK : Material.PLAYER_HEAD, + "§e" + op.getName(), + "§7金币: §a$" + String.format("%.2f", bal), + "§7债券: §e" + bondBal + " §6枚", + "", + "§a左键经济(±) §6右键债券(±)")); + slot++; + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + p.openInventory(g); + } + + public void openShop(Player p) { + p.closeInventory(); + plugin.getShopManager().openShopMain(p); + } + + public void openTicket(Player p) { + p.closeInventory(); + p.performCommand("sdf1_login ticket"); + } + @EventHandler + public void onInvClose( + + InventoryCloseEvent e) { + if (!(e.getPlayer() instanceof Player)) + return; + if (!(e.getPlayer() instanceof Player)) return; + Player p = (Player) e.getPlayer(); + Player p3 = (Player) e.getPlayer(); + String t = e.getView().getTitle(); + if (!(e.getPlayer() instanceof Player)) return; + Player p2 = (Player) e.getPlayer(); + + if (!t.equals(T_EDITOR)) return; + + // ★ 编辑器关闭时清理空项 ★ + List list = + plugin.getMenu().getItems(); + if (!list.isEmpty()) { + MenuManager.MenuItem last = + list.get(list.size() - 1); + if ((last.command == null + || last.command.trim().isEmpty() + || last.command.trim() + .equals("/")) + && "新菜单项" + .equals(last.title)) { + list.remove(list.size() - 1); + } + } + } + + public void openSubMenu( + Player p, String fileName) { + String menuTitle = + "§6§l" + fileName + .replace(".txt", ""); + Inventory g = Bukkit.createInventory( + null, 54, menuTitle); + fillBg(g); + + java.io.File f = new java.io.File( + plugin.getDataFolder(), fileName); + if (!f.exists()) { + p.sendMessage("§c文件不存在: " + + fileName); + return; + } + try { + List lines = + java.nio.file.Files + .readAllLines( + f.toPath(), + java.nio.charset + .StandardCharsets + .UTF_8); + List items = + new ArrayList<>(); + StringBuilder buf = + new StringBuilder(); + boolean inItem = false; + + for (String raw : lines) { + String l = raw.trim(); + if (l.startsWith("#") + || l.startsWith("//")) + continue; + if (l.contains("")) { + inHtml = false; + l = l.substring( + l.indexOf("-->") + 3) + .trim(); + } else { + plugin.getLogger().info( + "[Menu] L" + lineNum + + ": [HTML注释中]"); + continue; + } + } + + // ===== 第3优先:整行注释(最高裁决) ===== + // 以 # 或 // 开头的行,直接跳过 + // 不管后面包含什么 /* "); + pw.println(); + pw.println("{"); + pw.println(" 标题:主城传送"); + pw.println(" 指令:/spawn"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:COMPASS"); + pw.println("}"); + pw.println(); + pw.println("{"); + pw.println(" 标题:随机传送"); + pw.println(" 指令:/rtp"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:ENDER_PEARL"); + pw.println("}"); + } catch (Exception ignored) { + } + } + + // ===== 保存 ===== + public void saveMenu() { + File f = new File( + plugin.getDataFolder(), + "菜单.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 服务器菜单"); + pw.println(); + for (MenuItem mi : items) { + pw.println("{"); + pw.println(" 标题:" + + mi.title); + pw.println(" 指令:" + + mi.command); + pw.println(" 权限类型:" + + mi.permType); + // ★ 保存实际材质名 ★ + pw.println(" 图标:" + + mi.icon.getType().name()); + pw.println("}"); + pw.println(); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Menu] 保存失败: " + + e.getMessage()); + } + } + + // ===== 聊天编辑 ===== + public boolean isEditing(String name) { + return chatWait.contains(name); + } + + public void editTitle( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "title"); + p.closeInventory(); + p.sendMessage("§e请输入菜单标题:"); + } + + public void editCommand( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "command"); + p.closeInventory(); + p.sendMessage("§e请输入指令" + + "(自动补全/):"); + } + + public void onChat(Player p, String input) { + String name = p.getName(); + if (!chatWait.remove(name)) return; + + String field = chatField.remove(name); + Integer idx = chatIdx.get(name); + if (idx == null || idx >= items.size()) return; + + MenuItem mi = items.get(idx); + + if ("title".equals(field)) { + mi.title = input; + p.sendMessage("§a标题: " + input); + } else if ("command".equals(field)) { + if (!input.startsWith("/")) + input = "/" + input; + mi.command = input; + p.sendMessage("§a指令: " + input); + } + + Bukkit.getScheduler().runTask(plugin, () -> + plugin.gui.openEditor(p, idx)); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/OrderManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/OrderManager.java new file mode 100644 index 0000000..9dcb625 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/OrderManager.java @@ -0,0 +1,1345 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.logging.Level; + +public class OrderManager implements Listener { + private final Main plugin; + private final List orders = + Collections.synchronizedList(new ArrayList<>()); + private final Map userViewPage = new HashMap<>(); + private final Map adminViewPage = new HashMap<>(); + private final Map adminViewTarget = new HashMap<>(); + private final Set partialRefundListening = new HashSet<>(); + private final Map partialRefundTarget = new HashMap<>(); + private final Map receiptBuilderSection = new HashMap<>(); + private String tplHeader = ""; + private String tplItemLine = "{品名} x{数量} {原价}→{执行价} 小计{小计}枚"; + private String tplDiscountLine = "优惠: -{优惠额}枚 ({优惠类型})"; + private String tplPackLine = "打包: +{打包费}枚 ({打包颜色})"; + private String tplSummary = "原价{原价合计} → 实付{实付}"; + private String tplFooter = "感谢惠顾,欢迎再来!"; + private static final long RETENTION_30D = 30L * 24 * 60 * 60 * 1000; + private static final long SELF_REFUND_WINDOW = 5 * 60 * 1000; + private static final int PAGE_SIZE = 21; + private static final int[] SLOTS = { + 10,11,12,13,14,15,16,19,20,21,22,23,24,25,28,29,30,31,32,33,34}; + // ===== 字段 ===== + private final Set shulkerUsedOrders = + Collections.synchronizedSet(new HashSet<>()); + // ===== 待审批的退货退款 ===== + private final Map pendingReturns = + new HashMap<>(); + // ===== 退货申请确认 ===== + private final Map returnConfirmWaiting = + new HashMap<>(); // uuid -> orderId + private static final int RETURN_CONFIRM_TIMEOUT = 600; // 30秒 + + + private static class PendingReturn { + long orderId; + UUID playerUUID; + String playerName; + int totalPaid; + long createTime; + String packColor; + String packType; + List items; + + PendingReturn(OrderRecord r) { + this.orderId = r.orderId; + this.playerUUID = r.uuid; + this.playerName = r.player; + this.totalPaid = r.totalPaid; + this.createTime = System.currentTimeMillis(); + this.packColor = r.packColor; + this.packType = r.packType; + this.items = new ArrayList<>(r.items); + } + } + + + public static class OrderItem { + public String name; public String mat; + public int originalPrice; public int finalPrice; public int qty; + public OrderItem(String n,String m,int op,int fp,int q){ + name=n;mat=m;originalPrice=op;finalPrice=fp;qty=q;} + public int subtotal(){return finalPrice*qty;} + public int originalSubtotal(){return originalPrice*qty;} + } + public static class OrderRecord { + public long orderId; public UUID uuid; public String player; + public List items; public int totalOriginal; public int totalPaid; + public int discount; public String discountType; public String couponCode; + public int packFee; public String packType; public String packColor; + public long timestamp; public long printTime; public int status; + public int refundedAmount; public String refundType; + } + + public OrderManager(Main plugin) { + this.plugin = plugin; + loadReceiptConfig(); + loadOrders(); + startCleanupTask(); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + public List getOrders() { return orders; } + + // ===== 小票模板 ===== + private void loadReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + if (!f.exists()) createDefaultReceiptConfig(f); + Map m = loadMap(f); + tplHeader = m.getOrDefault("头部", tplHeader); + tplItemLine = m.getOrDefault("商品行", tplItemLine); + tplDiscountLine = m.getOrDefault("优惠行", tplDiscountLine); + tplPackLine = m.getOrDefault("打包行", tplPackLine); + tplSummary = m.getOrDefault("汇总", tplSummary); + tplFooter = m.getOrDefault("页脚", tplFooter); + } + private void saveReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=" + tplHeader); + L.add("商品行=" + tplItemLine); + L.add("优惠行=" + tplDiscountLine); + L.add("打包行=" + tplPackLine); + L.add("汇总=" + tplSummary); + L.add("页脚=" + tplFooter); + writeLines(f, L); + } + private void createDefaultReceiptConfig(File f) { + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}"); + L.add("商品行=§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚"); + L.add("优惠行=优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})"); + L.add("打包行=打包: §c+{打包费}枚§r ({打包颜色})"); + L.add("汇总=原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚"); + L.add("页脚=感谢惠顾,欢迎再来!"); + writeLines(f, L); + } + + // ===== 订单记录 ===== + public void recordOrder(Player p, List items, + int totalOriginal, int totalPaid, int discount, + String discountType, String couponCode, int packFee, + String packType, String packColor) { + OrderRecord r = new OrderRecord(); + r.orderId = System.currentTimeMillis(); + r.uuid = p.getUniqueId(); r.player = p.getName(); + r.items = items; r.totalOriginal = totalOriginal; + r.totalPaid = totalPaid; r.discount = discount; + r.discountType = discountType; r.couponCode = couponCode; + r.packFee = packFee; r.packType = packType; + r.packColor = packColor; r.timestamp = r.orderId; + r.printTime = 0; r.status = 0; r.refundedAmount = 0; + r.refundType = null; + orders.add(r); saveOrders(); + } + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + // ===== 小票聊天栏 ===== + /** + * 聊天栏打印小票(给定参数 Player + OrderRecord) + * 不要改动方法签名 + */ + public void sendReceiptChat(Player p, OrderRecord r) { + if (p == null || r == null) return; + + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + p.sendMessage("§7§m "); + p.sendMessage("§6§l======== 商店购物小票 ========"); + + // 退款标记 + String statusTag = getStatusTag(r); + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§7订单号: §f#" + r.orderId); + p.sendMessage("§7下单时间: §f" + + df.format(new Date(r.timestamp))); + p.sendMessage("§7玩家: §f" + r.player); + p.sendMessage("§7------------------------------"); + + for (OrderItem it : r.items) { + String price; + if (it.originalPrice != it.finalPrice) { + price = "§f" + it.originalPrice + + "§7→§6" + it.finalPrice; + } else { + price = "§6" + it.finalPrice; + } + p.sendMessage("§7 §e" + it.name + + " §7x" + it.qty + + " " + price + "枚" + + " §6小计" + it.subtotal() + "枚"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7原价: §f" + r.totalOriginal + "枚"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "§7(优惠券 -" + pct + "%)" + : "eco".equals(r.discountType) + ? "§7(环保 -" + pct + "%)" + : "§7(-" + pct + "%)"; + p.sendMessage("§7优惠: §a-" + r.discount + + "枚 " + dt); + } + + if (r.packFee > 0) { + String cn = r.packColor != null + ? r.packColor + .replace("_SHULKER_BOX", "") + .replace("SHULKER_BOX", "原色") + : "默认"; + p.sendMessage("§7打包: §c+" + r.packFee + + "枚 §7(" + cn + "潜影盒)"); + } else { + p.sendMessage("§7包装: 直接放入背包"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7实付: §6" + r.totalPaid + "枚"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + p.sendMessage("§7余额: §a" + bal + "枚"); + + // 退款标记 + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§6§l=============================="); + r.printTime = System.currentTimeMillis(); + } + + /** + * 生成书本小票内容 + */ + private String buildReceiptText(OrderRecord r) { + String statusTag = getStatusTag(r); + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + StringBuilder sb = new StringBuilder(); + + sb.append("§6§l===== 商店小票 =====\n"); + + // 退款标记 + String tag = getStatusTag(r); + if (!tag.isEmpty()) sb.append(tag).append("\n"); + + sb.append("订单号: #").append(r.orderId).append("\n"); + sb.append("时间: ").append( + df.format(new Date(r.timestamp))).append("\n"); + sb.append("玩家: ").append(r.player).append("\n"); + sb.append("------------------------------\n"); + + for (OrderItem it : r.items) { + String pr = (it.originalPrice != it.finalPrice) + ? it.originalPrice + "->" + it.finalPrice + : String.valueOf(it.finalPrice); + sb.append(it.name).append(" x").append(it.qty) + .append(" ").append(pr).append("枚") + .append(" 小计").append(it.subtotal()) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("原价: ").append(r.totalOriginal) + .append("枚\n"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "优惠券(-" + pct + "%)" + : "环保(-" + pct + "%)"; + sb.append("优惠: -").append(r.discount) + .append("枚 ").append(dt).append("\n"); + } + + if (r.packFee > 0) { + sb.append("打包: +").append(r.packFee) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("实付: ").append(r.totalPaid).append("枚\n"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + sb.append("余额: ").append(bal).append("枚\n"); + + // 退款标记 + if (!statusTag.isEmpty()) sb.append("\n").append(tag).append("\n"); + + return sb.toString(); + } + + /** + * 退款状态标记文本 + */ + private String getStatusTag(OrderRecord r) { + switch (r.status) { + case 2: + return "§c§l<<<<<<< 已全额退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 3: + return "§c§l<<<<<<< 已部分退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 4: + return "§c§l<<<<<<< 退款申请已拒绝 >>>>>>>"; + default: + return ""; + } + } + + /** + * 创建小票书 + */ + public ItemStack createReceiptBook(OrderRecord r) { + String text = buildReceiptText(r); + String[] lines = text.split("\n"); + + ItemStack book = new ItemStack(Material.WRITTEN_BOOK); + BookMeta bm = (BookMeta) book.getItemMeta(); + bm.setTitle("购物小票 #" + r.orderId); + bm.setAuthor("商店"); + + StringBuilder page = new StringBuilder(); + int lineCount = 0; + for (String line : lines) { + // ★ 保留颜色代码 + page.append(line).append("\n"); + lineCount++; + if (lineCount >= 13) { + bm.addPage(page.toString().trim()); + page = new StringBuilder(); + lineCount = 0; + } + } + if (page.length() > 0) { + bm.addPage(page.toString().trim()); + } + + book.setItemMeta(bm); + return book; + } + + // ===== 退款工具 ===== + private boolean withinSelfRefundWindow(OrderRecord r) { return System.currentTimeMillis() - r.timestamp < SELF_REFUND_WINDOW; } + private int countDirectItems(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private int countShulkers(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private void removeItems(Player p, String matName, int amt) { + Material m = Material.matchMaterial(matName); if (m != null) p.getInventory().removeItem(new ItemStack(m, amt)); + } + private boolean removeOneShulker(Player p, + String materialName) { + // 先尝试精确匹配 + Material mat = Material.matchMaterial(materialName); + if (mat != null) { + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + return true; + } + } + } + // 匹配不到,移除任意一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .endsWith("_SHULKER_BOX")) { + p.getInventory().clear(i); + return true; + } + } + return false; + } + public String validateRefund(OrderRecord r, Player p) { + if (r.status != 0) + return "订单状态: " + getStatusName(r.status); + + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // ★ 检查:潜影盒是否被放置过 + if (shulkerUsedOrders.contains(r.orderId)) { + return "§c潜影盒已被使用过,无法自助退款"; + } + + // ★ 检查:背包里是否有同一订单的潜影盒 + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im == null || im.getLore() == null) + continue; + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + return null; // 找到了,可退 + } + } + } + return "§c对应的潜影盒已不在背包中"; + } else { + // ★ 散装:检测物品数量 + for (OrderItem it : r.items) { + int have = countDirectItems(p, it.mat); + if (have < it.qty) + return "§c" + it.name + "不足(有" + + have + "/" + it.qty + ")"; + } + } + return null; + } + + + + private void notifyPlayer(UUID uuid, String msg) { Player p = Bukkit.getPlayer(uuid); if (p != null && p.isOnline()) p.sendMessage(msg); } + private void notifyAdmins(Player p, OrderRecord r) { + String m = "§e[订单] " + p.getName() + " 申请退款 #" + r.orderId + " §6" + r.totalPaid + "枚"; + for (Player a : Bukkit.getOnlinePlayers()) if (isAdmin(a) && !a.equals(p)) a.sendMessage(m); + } + private String getStatusName(int s) { switch(s){case 0:return "正常";case 1:return "申请中";case 2:return "已退";case 3:return "部分退";case 4:return "已拒绝";default:return "未知";} } + private boolean isAdmin(Player p) { + if (p.isOp()) return true; + try { ConfigManager cm = plugin.getConfigMgr(); if (cm == null) return false; return p.hasPermission("sdf1.admin") || p.hasPermission("sdf1." + cm.adminTag); } catch (Exception e) { return p.hasPermission("sdf1.admin"); } + } + + // ===== 订单中心 ===== + public void openOrderCenter(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l订单中心"); + g.setItem(13, mkItem(Material.BOOK, "§e§l购买记录", "§7查看历史订单", "", "§a点击进入")); + if (isAdmin(p)) { g.setItem(31, mkItem(Material.NETHER_STAR, "§c§l管理员面板", "§7在线玩家购物记录", "§7支持退款操作", "", "§a点击进入")); + g.setItem(33, mkItem(Material.CRAFTING_TABLE, "§e§l小票配置", "§7配置小票模板样式", "", "§a点击进入")); } + g.setItem(49, mkItem(Material.ARROW, "§7返回商店")); + p.openInventory(g); + } + + // ===== 购买记录 ===== + public void openMyOrders(Player p, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l购买记录"); + List list = getPlayerAllOrders(p.getUniqueId()); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + userViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon; String st; String act; + if (r.status != 0) { icon = r.status == 1 ? Material.PAPER : Material.BARRIER; st = "§7状态: " + getStatusName(r.status); act = "§b左键: 补打小票"; } + else if (withinSelfRefundWindow(r)) { icon = Material.EMERALD; st = "§7状态: §a可退款(5分钟内)"; act = "§b左键: 补打小票\n§a右键: 退款退货\n§eShift+右键: 仅退款"; } + else { icon = Material.BOOK; st = "§7状态: §7已过自助退款期"; act = "§b左键: 补打小票\n§e右键: 申请退款退货\n§6Shift+右键: 申请仅退款"; } + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7原价: §f" + r.totalOriginal + "枚"); + if (r.discount > 0) lore.add("§7优惠: §a-" + r.discount + "枚"); + lore.add("§7实付: §6" + r.totalPaid + "枚"); lore.add(st); lore.add(""); + for (String line : act.split("\n")) lore.add(line); + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7暂无订单记录")); + addPageButtons(g, page, total); + p.openInventory(g); + } + + // ===== 管理员面板 ===== + public void openAdminPanel(Player p, int page) { + if (!isAdmin(p)) { p.sendMessage("§c权限不足"); return; } + Inventory g = Bukkit.createInventory(null, 54, "§c§l管理员订单面板"); + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int total = Math.max(1, (int) Math.ceil((double) online.length / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); adminViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, online.length); + // ★ 替换 openAdminPanel 中的物品创建部分 + for (int i = start; i < end; i++) { + int si = i - start; + if (si >= SLOTS.length) break; + Player t = online[i]; + List tl = getPlayerOrdersWithin( + t.getUniqueId(), RETENTION_30D); + int ts = 0; + for (OrderRecord r : tl) ts += r.totalPaid; + + ItemStack is = new ItemStack(Material.PLAYER_HEAD); + org.bukkit.inventory.meta.SkullMeta sm = + (org.bukkit.inventory.meta.SkullMeta) + is.getItemMeta(); + if (sm != null) { + sm.setOwner(t.getName()); // ★ 设置头颅皮肤 + sm.setDisplayName("§e" + t.getName()); + sm.setLore(Arrays.asList( + "§7订单: §f" + tl.size() + "笔", + "§7消费: §6" + ts + "枚", + "", "§a点击进入")); + is.setItemMeta(sm); // ★ 这行之前缺失了 + } + g.setItem(SLOTS[si], is); + } + + if (online.length == 0) g.setItem(31, mkItem(Material.BARRIER, "§7无在线玩家")); + addPageButtons(g, page, total); p.openInventory(g); + } + + // ===== 管理员订单 ===== + public void openAdminOrders(Player p, UUID targetUUID, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§c§l订单管理"); + String name = Bukkit.getOfflinePlayer(targetUUID).getName(); + List list = getPlayerOrdersWithin(targetUUID, RETENTION_30D); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + adminViewPage.put(p.getUniqueId(), page); adminViewTarget.put(p.getUniqueId(), targetUUID); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon = r.status == 2 || r.status == 4 ? Material.BARRIER : r.status == 1 ? Material.PAPER : Material.BOOK; + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7实付: §6" + r.totalPaid + "枚"); + if (r.status == 1) { lore.add("§7状态: §e申请中(" + nn(r.refundType) + ")"); lore.add(""); lore.add("§a右键: 批准"); lore.add("§c左键: 拒绝"); } + else if (r.status == 2) lore.add("§7已退: §a" + r.refundedAmount + "枚"); + else if (r.status == 3) lore.add("§7部分退: §a" + r.refundedAmount + "枚"); + else if (r.status == 4) lore.add("§7已拒绝"); + else { lore.add(""); lore.add("§a右键: 整单退款"); lore.add("§e左键: 部分退款(输入%)"); } + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7该玩家暂无订单")); + addPageButtons(g, page, total); + g.setItem(49, mkItem(org.bukkit.Material.PAPER, name + " §e" + (page + 1) + "/" + total)); + p.openInventory(g); + } + private void addPageButtons(Inventory g, int page, int total) { + g.setItem(48, page > 0 ? mkItem(Material.ARROW, "§7上一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + if (g.getItem(49) == null) g.setItem(49, mkItem(Material.PAPER, "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 ? mkItem(Material.ARROW, "§7下一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, "§7返回")); + } + + // ===== 小票配置 ===== + public void openReceiptConfig(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l小票配置"); + g.setItem(10, mkItem(Material.PAPER, "§e§l头部", "§7" + brief(tplHeader), "", "§a点击编辑")); + g.setItem(11, mkItem(Material.PAPER, "§e§l商品行", "§7" + brief(tplItemLine), "", "§a点击编辑")); + g.setItem(12, mkItem(Material.PAPER, "§e§l优惠行", "§7" + brief(tplDiscountLine), "", "§a点击编辑")); + g.setItem(14, mkItem(Material.PAPER, "§e§l打包行", "§7" + brief(tplPackLine), "", "§a点击编辑")); + g.setItem(15, mkItem(Material.PAPER, "§e§l汇总", "§7" + brief(tplSummary), "", "§a点击编辑")); + g.setItem(16, mkItem(Material.PAPER, "§e§l页脚", "§7" + brief(tplFooter), "", "§a点击编辑")); + g.setItem(22, mkItem(Material.BOOK, "§6§l预览(随机价格)", "§7预览小票效果")); + g.setItem(48, mkItem(Material.LIME_WOOL, "§a§l保存")); g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(50, mkItem(Material.RED_WOOL, "§c§l重置")); + p.openInventory(g); + } + public void openReceiptBuilder(Player p, String section) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l编辑: " + sectionName(section)); + g.setItem(10, mkItem(Material.EMERALD, "§a{品名}")); g.setItem(11, mkItem(Material.EMERALD, "§a{原价}")); + g.setItem(12, mkItem(Material.EMERALD, "§a{执行价}")); g.setItem(13, mkItem(Material.EMERALD, "§a{数量}")); + g.setItem(14, mkItem(Material.EMERALD, "§a{小计}")); g.setItem(15, mkItem(Material.EMERALD, "§a{商店名}")); + g.setItem(16, mkItem(Material.EMERALD, "§a{玩家}")); g.setItem(19, mkItem(Material.DIAMOND, "§b{订单号}")); + g.setItem(20, mkItem(Material.DIAMOND, "§b{订单时间}")); g.setItem(21, mkItem(Material.DIAMOND, "§b{打印时间}")); + g.setItem(22, mkItem(Material.DIAMOND, "§b{日期}")); g.setItem(23, mkItem(Material.DIAMOND, "§b{时间}")); + g.setItem(24, mkItem(Material.DIAMOND, "§b{原价合计}")); g.setItem(25, mkItem(Material.DIAMOND, "§b{实付}")); + g.setItem(28, mkItem(Material.GOLD_INGOT, "§6{优惠额}")); g.setItem(29, mkItem(Material.GOLD_INGOT, "§6{优惠类型}")); + g.setItem(30, mkItem(Material.GOLD_INGOT, "§6{优惠券码}")); g.setItem(31, mkItem(Material.GOLD_INGOT, "§6{打包费}")); + g.setItem(32, mkItem(Material.GOLD_INGOT, "§6{打包颜色}")); g.setItem(33, mkItem(Material.GOLD_INGOT, "§6{余额}")); + g.setItem(37, mkItem(Material.STONE, "§f═══")); g.setItem(38, mkItem(Material.STONE, "§f───")); + g.setItem(39, mkItem(Material.STONE, "§f═════════════")); g.setItem(40, mkItem(Material.PAPER, "§e换行(\\n)")); + g.setItem(47, mkItem(Material.RED_STAINED_GLASS_PANE, "§c删末尾")); g.setItem(48, mkItem(Material.RED_WOOL, "§c清空")); + g.setItem(49, mkItem(Material.ARROW, "§7返回")); g.setItem(50, mkItem(Material.LIME_WOOL, "§a保存")); + g.setItem(51, mkItem(Material.BLUE_WOOL, "§9重置")); + p.openInventory(g); + } + // ===== 点击处理 ===== + public boolean handleCenterClick(Player p, int raw) { + if (raw == 13) { openMyOrders(p, 0); return true; } + if (raw == 31 && isAdmin(p)) { openAdminPanel(p, 0); return true; } + if (raw == 33 && isAdmin(p)) { openReceiptConfig(p); return true; } + if (raw == 49) { plugin.getShopManager().openShopMain(p); return true; } + return true; + } + public boolean handleMyOrdersClick(Player p, int raw, boolean left, boolean shift) { + List list = getPlayerAllOrders(p.getUniqueId()); + int page = userViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openMyOrders(p, page - 1); return true; } + if (raw == 50) { openMyOrders(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (left) { + try { ItemStack book = createReceiptBook(r); + HashMap ov = p.getInventory().addItem(book); + for (ItemStack d : ov.values()) p.getWorld().dropItemNaturally(p.getLocation(), d); + r.printTime = System.currentTimeMillis(); p.sendMessage("§a已补打小票书: #" + r.orderId); + } catch (Exception e) { p.sendMessage("§c补打失败"); } + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // ★ Shift+右键:仅退款申请(需管理员审批) + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请,等待管理员审批"); + notifyAdmins(p, r); + } else { + // ★ 普通右键:5分钟内直接退(自动通过) + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason + + ",Shift+右键可申请仅退款"); + } else { + // 扣货 + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + if (isShulker) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory() + .getItem(i); + if (is != null && is.getType() + .name().contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + // 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + } + + + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // 仅退款申请 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } else { + // 5分钟内退货退款 + String reason = validateRefund(r, p); + if (reason != null) { + // ★ 不满足条件:询问是否申请 + p.sendMessage("§c" + reason); + askReturnConfirm(p, r); + } else { + // 满足条件:自动退 + doAutoReturn(p, r); + } + } + } else { + if (shift) { + // 申请仅退款 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请"); + notifyAdmins(p, r); + } else { + // ★ 申请退货退款:先检查条件 + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason); + } + // ★ 无论是否满足条件都询问 + askReturnConfirm(p, r); + } + } + } + } + if (left) { + // 已退款订单仍然允许补打小票,但小票内会显示退款标记 + try { + ItemStack book = createReceiptBook(r); + HashMap overflow = + p.getInventory().addItem(book); + for (ItemStack drop : overflow.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + r.printTime = System.currentTimeMillis(); + if (r.status != 0) { + p.sendMessage("§e已补打小票(含退款标记): #" + + r.orderId); + } else { + p.sendMessage("§a已补打小票书: #" + + r.orderId); + } + } catch (Exception e) { + p.sendMessage("§c补打失败"); + } + } + + final int fp = page; Bukkit.getScheduler().runTask(plugin, () -> openMyOrders(p, fp)); + return true; + } + public boolean handleAdminClick(Player p, int raw) { + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openAdminPanel(p, page - 1); return true; } + if (raw == 50) { openAdminPanel(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= online.length) return true; + openAdminOrders(p, online[actual].getUniqueId(), 0); return true; + } + public boolean handleAdminOrdersClick(Player p, int raw, boolean left, boolean shift) { + UUID tu = adminViewTarget.get(p.getUniqueId()); + if (tu == null) return false; + List list = getPlayerOrdersWithin(tu, RETENTION_30D); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { + openAdminOrders(p, tu, page - 1); + return true; + } + if (raw == 50) { + openAdminOrders(p, tu, page + 1); + return true; + } + if (raw == 53) { + openAdminPanel(p, 0); + return true; + } + int si = slotIndex(raw); + if (si < 0) return true; + int actual = si + page * PAGE_SIZE; + if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (r.status == 2 || r.status == 3 || r.status == 4) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + return true; + } + if (left) { + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款被拒绝"); + openAdminOrders(p, tu, page); + } else if (r.status == 0) { + partialRefundListening.add(p.getUniqueId()); + partialRefundTarget.put(p.getUniqueId(), r); + p.closeInventory(); + p.sendMessage("§e输入退款百分比(1-100): #" + r.orderId + " 实付" + r.totalPaid + "枚"); + } + } else { + if (r.status == 0 || r.status == 1) { + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a全额退款 §e" + r.player + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, "§a退款 §6" + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + } + } + // ★ 替换 handleAdminOrdersClick 中的审批逻辑 + if (left) { + // 左键 = 拒绝 + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款申请已被拒绝"); + openAdminOrders(p, tu, page); + } + } else { + // 右键 = 批准 + if (r.status == 1) { + String rt = r.refundType != null + ? r.refundType : ""; + + // ★ 退款退货申请 → 检查潜影盒 + if (rt.equals("refund_return_apply")) { + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 检查玩家背包里有没有带订单标签的盒子 + Player target = Bukkit.getPlayer(r.uuid); + if (target == null || !target.isOnline()) { + p.sendMessage("§c玩家不在线,无法执行退货退款"); + openAdminOrders(p, tu, page); + return true; + } + + boolean foundTagged = false; + boolean foundAny = false; + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is == null) continue; + if (!is.getType().name() + .contains("SHULKER")) continue; + foundAny = true; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + foundTagged = true; + // 收走盒子 + target.getInventory().clear(i); + break; + } + } + } + if (foundTagged) break; + } + + if (foundTagged) { + // ★ 有带标记的盒子 → 收走 + 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + + r.player + " §6" + + r.totalPaid + "枚(盒子已收走)"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + return true; + } else if (foundAny) { + // ★ 有盒子但没标记 → 等管理员决定 + pendingReturns.put(r.orderId, + new PendingReturn(r)); + p.closeInventory(); + p.sendMessage("§e§l[退货审批] 订单 #" + + r.orderId); + p.sendMessage("§7玩家背包有潜影盒但" + + "无订单标记,可能已被替换"); + p.sendMessage("§a输入 §e1 §a= 收走盒子并退款"); + p.sendMessage("§e输入 §e2 §e= 仅退款(不收盒子)"); + p.sendMessage("§c输入 §c3 §c= 拒绝退款"); + p.sendMessage("§72分钟内未操作自动拒绝"); + // 启动2分钟超时 + startReturnTimeout(r.orderId); + return true; + } else { + // ★ 背包里没有任何盒子 + p.sendMessage("§c玩家背包里没有潜影盒"); + p.sendMessage("§7可拒绝并通知玩家" + + "申请仅退款或把货拿回来"); + openAdminOrders(p, tu, page); + return true; + } + } + // ★ 散装退货退款:扣货+退钱 + Player target = Bukkit.getPlayer(r.uuid); + if (target != null && target.isOnline()) { + for (OrderItem it : r.items) + removeItems(target, it.mat, it.qty); + } + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + return true; + } + + // ★ 仅退款申请 / 5分钟内自动退款 + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已批准退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退款已批准,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + } + } + return true; + } + + /** + * 询问用户是否提交退货退款申请 + */ + private void askReturnConfirm(Player p, OrderRecord r) { + returnConfirmWaiting.put(p.getUniqueId(), r.orderId); + p.sendMessage(""); + p.sendMessage("§e§l[退货退款申请]"); + p.sendMessage("§7订单 #" + r.orderId + + " 实付" + r.totalPaid + "枚"); + p.sendMessage("§a输入 §e1 §a= 提交退货退款申请"); + p.sendMessage("§c输入 §e2 §c= 取消"); + p.sendMessage("§730秒内未操作自动取消"); + p.sendMessage(""); + + // 30秒超时 + final UUID uuid = p.getUniqueId(); + final long oid = r.orderId; + new BukkitRunnable() { + @Override + public void run() { + Long waiting = returnConfirmWaiting.get(uuid); + if (waiting != null && waiting == oid) { + returnConfirmWaiting.remove(uuid); + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§7退货退款申请已超时取消"); + } + } + } + }.runTaskLater(plugin, RETURN_CONFIRM_TIMEOUT); + } + + /** + * 执行5分钟内自动退货退款 + */ + private void doAutoReturn(Player p, OrderRecord r) { + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 找并收走带标签的盒子 + boolean found = false; + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + p.getInventory().clear(i); + found = true; + break; + } + } + } + if (found) break; + } + // 没找到标签盒子,收任意潜影盒 + if (!found) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + + + // ===== 2分钟超时 ===== + private void startReturnTimeout(long orderId) { + new BukkitRunnable() { + @Override + public void run() { + PendingReturn pr = pendingReturns + .remove(orderId); + if (pr == null) return; // 已处理 + Player admin = Bukkit.getPlayer(pr.playerUUID); + if (admin != null && admin.isOnline()) { + admin.sendMessage("§c[退货审批] 订单 #" + + orderId + " 超时,自动拒绝"); + } + // 通知玩家 + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款申请已超时拒绝"); + // 恢复订单状态 + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 0; + r.refundType = null; + saveOrders(); + } + } + }.runTaskLater(plugin, 2400L); // 2分钟 + } + + // ===== 查找订单 ===== + private OrderRecord findOrder(long orderId) { + synchronized (orders) { + for (OrderRecord r : orders) { + if (r.orderId == orderId) return r; + } + } + return null; + } + + + public boolean handleReceiptConfigClick(Player p, int raw) { + if (raw == 49) { openOrderCenter(p); return true; } + if (raw == 48) { saveReceiptConfig(); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 50) { createDefaultReceiptConfig(new File(plugin.getDataFolder(), "小票设置.txt")); loadReceiptConfig(); p.sendMessage("§e已重置"); openReceiptConfig(p); return true; } + if (raw == 22) { sendRandomPreview(p); return true; } + String sec = null; + if (raw == 10) sec = "header"; else if (raw == 11) sec = "itemLine"; else if (raw == 12) sec = "discountLine"; + else if (raw == 14) sec = "packLine"; else if (raw == 15) sec = "summary"; else if (raw == 16) sec = "footer"; + if (sec != null) { receiptBuilderSection.put(p.getUniqueId(), sec); openReceiptBuilder(p, sec); } + return true; + } + public boolean handleReceiptBuilderClick(Player p, int raw) { + String sec = receiptBuilderSection.get(p.getUniqueId()); if (sec == null) return false; + String token = getTokenBySlot(raw); + if (token != null) { String cur = getSection(sec); setSection(sec, cur.isEmpty() ? token : cur + token); openReceiptBuilder(p, sec); return true; } + if (raw == 47) { String c = getSection(sec); int lb = c.lastIndexOf('}'); if (lb >= 0) { int ls = c.lastIndexOf('{', lb); if (ls >= 0) setSection(sec, c.substring(0, ls)); } else setSection(sec, c.substring(0, Math.max(0, c.length() - 8))); openReceiptBuilder(p, sec); return true; } + if (raw == 48) { setSection(sec, ""); openReceiptBuilder(p, sec); return true; } + if (raw == 49) { receiptBuilderSection.remove(p.getUniqueId()); openReceiptConfig(p); return true; } + if (raw == 50) { saveReceiptConfig(); receiptBuilderSection.remove(p.getUniqueId()); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 51) { setSection(sec, getDefaultVal(sec)); openReceiptBuilder(p, sec); return true; } + return true; + } + + // ===== 聊天事件 ===== + @EventHandler + public void onAdminChat(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); UUID uuid = p.getUniqueId(); + // ★ 在 onAdminChat 方法开头添加 +// ===== 退货审批输入 ===== + for (Map.Entry entry : + new HashMap<>(pendingReturns).entrySet()) { + Long pendingOrderId = entry.getKey(); + PendingReturn pr = entry.getValue(); + if (pr.playerUUID.equals(p.getUniqueId()) + || isAdmin(p)) { + try { + int choice = Integer.parseInt( + e.getMessage().trim()); + pendingReturns.remove(pendingOrderId); + OrderRecord r = findOrder(pendingOrderId); + if (r == null) { + p.sendMessage("§c订单不存在"); + return; + } + + if (choice == 1) { + // ★ 收走盒子并退款 + Player target = Bukkit.getPlayer( + pr.playerUUID); + if (target != null && target.isOnline()) { + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + pendingOrderId)) { + target.getInventory().clear(i); + break; + } + } + } + break; + } + } + } + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(盒子已收走)"); + notifyPlayer(pr.playerUUID, + "§a[订单] 退货退款成功,§6" + + pr.totalPaid + "枚已退还"); + + } else if (choice == 2) { + // ★ 仅退款(不收盒子) + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§e已仅退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(未收盒子)"); + notifyPlayer(pr.playerUUID, + "§e[订单] 仅退款成功,§6" + + pr.totalPaid + "枚已退还" + + ",盒子无需退回"); + + } else if (choice == 3) { + // ★ 拒绝 + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款 #" + pendingOrderId); + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款已被拒绝" + + ",您可以申请仅退款" + + "或把货拿回来后重新申请"); + } else { + p.sendMessage("§c请输入 1、2 或 3"); + pendingReturns.put(pendingOrderId, pr); + } + return; + + } catch (NumberFormatException ignored) { + // 不是数字,跳过 + } + } + } +// ★ 在 onAdminChat 方法开头,处理用户退货确认 + if (returnConfirmWaiting.containsKey(uuid)) { + String msg = e.getMessage().trim(); + if (msg.equals("1")) { + // ★ 确认提交 + Long orderId = returnConfirmWaiting.remove(uuid); + if (orderId != null) { + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 1; + r.refundType = "refund_return_apply"; + saveOrders(); + p.sendMessage("§e已提交退货退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } + } + e.setCancelled(true); + return; + } else if (msg.equals("2")) { + // ★ 取消 + returnConfirmWaiting.remove(uuid); + p.sendMessage("§7已取消退货退款申请"); + e.setCancelled(true); + return; + } else { + // 输入其他内容:取消等待 + returnConfirmWaiting.remove(uuid); + // 不 return,让消息正常发送 + } + } + + if (partialRefundListening.contains(uuid)) { + e.setCancelled(true); partialRefundListening.remove(uuid); + OrderRecord t = partialRefundTarget.remove(uuid); if (t == null) return; + try { int pct = Integer.parseInt(e.getMessage().trim()); if (pct < 1 || pct > 100) { p.sendMessage("§c1-100"); return; } + int ref = t.totalPaid * pct / 100; if (ref <= 0) ref = 1; + plugin.getBonds().addBonds(t.player, ref); t.status = 3; t.refundedAmount = ref; saveOrders(); + p.sendMessage("§a部分退款 §e" + t.player + " §6" + ref + "枚(" + pct + "%)"); + notifyPlayer(t.uuid, "§a退款 §6" + ref + "枚"); + final UUID tu = adminViewTarget.get(uuid); final int pg = adminViewPage.getOrDefault(uuid, 0); + Bukkit.getScheduler().runTask(plugin, () -> openAdminOrders(p, tu, pg)); + } catch (NumberFormatException ex) { p.sendMessage("§c请输入数字"); } catch (Exception ex) { plugin.getLogger().log(Level.WARNING, "[Order]退款异常", ex); } + return; + } + if (receiptBuilderSection.containsKey(uuid)) { + e.setCancelled(true); String sec = receiptBuilderSection.remove(uuid); if (sec == null) return; + String msg = e.getMessage().trim(); + if ("cancel".equalsIgnoreCase(msg)) { Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); return; } + setSection(sec, msg.replace("\\n", "\n")); p.sendMessage("§a已更新 §e" + sec); + Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); + } + } + + // ===== 数据查询 ===== + private List getPlayerAllOrders(UUID uuid) { + List r = new ArrayList<>(); + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && System.currentTimeMillis() - o.timestamp < RETENTION_30D) r.add(o); } + Collections.reverse(r); return r; + } + private List getPlayerOrdersWithin(UUID uuid, long ms) { + List r = new ArrayList<>(); long cut = System.currentTimeMillis() - ms; + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && o.timestamp >= cut) r.add(o); } + Collections.reverse(r); return r; + } + + // ===== 持久化 ===== + private void loadOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); if (!f.exists()) return; + try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); + OrderRecord cur = null; String line; + while ((line = br.readLine()) != null) { line = line.trim(); + if (line.equals("==")) { cur = new OrderRecord(); cur.items = new ArrayList<>(); } + else if (cur != null && line.contains("=")) { int eq = line.indexOf('='); String k = line.substring(0, eq); String v = line.substring(eq + 1); + switch (k) { case "id": cur.orderId = Long.parseLong(v); cur.timestamp = cur.orderId; break; case "uuid": cur.uuid = UUID.fromString(v); break; + case "player": cur.player = v; break; case "totalOriginal": cur.totalOriginal = Integer.parseInt(v); break; case "totalPaid": cur.totalPaid = Integer.parseInt(v); break; + case "discount": cur.discount = Integer.parseInt(v); break; case "discountType": cur.discountType = v; break; case "couponCode": cur.couponCode = v; break; + case "packFee": cur.packFee = Integer.parseInt(v); break; case "packType": cur.packType = v; break; case "packColor": cur.packColor = v; break; + case "status": cur.status = Integer.parseInt(v); break; case "refundedAmount": cur.refundedAmount = Integer.parseInt(v); break; + case "printTime": cur.printTime = Long.parseLong(v); break; case "refundType": cur.refundType = v; break; + case "item": String[] pp = v.split("\\|"); if (pp.length >= 5) cur.items.add(new OrderItem(pp[0], pp[1], Integer.parseInt(pp[2]), Integer.parseInt(pp[3]), Integer.parseInt(pp[4]))); break; } + } else if (line.isEmpty() && cur != null && cur.uuid != null) { orders.add(cur); cur = null; } + } if (cur != null && cur.uuid != null) orders.add(cur); br.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]加载失败: " + e.getMessage()); } + } + private void saveOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); + try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); + synchronized (orders) { for (OrderRecord r : orders) { pw.println("=="); + pw.println("id=" + r.orderId); pw.println("uuid=" + r.uuid); pw.println("player=" + r.player); + pw.println("totalOriginal=" + r.totalOriginal); pw.println("totalPaid=" + r.totalPaid); + pw.println("discount=" + r.discount); pw.println("discountType=" + nn(r.discountType)); + pw.println("couponCode=" + nn(r.couponCode)); pw.println("packFee=" + r.packFee); + pw.println("packType=" + nn(r.packType)); pw.println("packColor=" + nn(r.packColor)); + pw.println("status=" + r.status); pw.println("refundedAmount=" + r.refundedAmount); + pw.println("printTime=" + r.printTime); pw.println("refundType=" + nn(r.refundType)); + for (OrderItem it : r.items) pw.println("item=" + it.name + "|" + it.mat + "|" + it.originalPrice + "|" + it.finalPrice + "|" + it.qty); + } } pw.flush(); pw.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]保存失败: " + e.getMessage()); } + } + private void startCleanupTask() { new BukkitRunnable() { public void run() { synchronized (orders) { if (orders.removeIf(OrderManager.this::isExpired)) saveOrders(); } } }.runTaskTimer(plugin, 36000L, 36000L); } + /** + * 将小票书塞入潜影盒 + */ + public void addBookToShulker(ItemStack shulker, ItemStack book) { + try { + org.bukkit.inventory.meta.BlockStateMeta bm = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (bm == null) return; + org.bukkit.block.BlockState st = bm.getBlockState(); + if (st instanceof org.bukkit.block.Container) { + ((org.bukkit.block.Container) st) + .getInventory().addItem(book); + bm.setBlockState(st); + shulker.setItemMeta(bm); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Order] 塞书失败: " + e.getMessage()); + } + } + + // ===== 工具 ===== + private String nn(String s) { return s != null ? s : ""; } + private boolean isExpired(OrderRecord r) { return System.currentTimeMillis() - r.timestamp > RETENTION_30D; } + private String fmtDate(long ts) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(ts)); } + private int slotIndex(int raw) { for (int i = 0; i < SLOTS.length; i++) if (SLOTS[i] == raw) return i; return -1; } + private String brief(String s) { if (s == null || s.isEmpty()) return "(空)"; String c = sc(s); return c.length() > 30 ? c.substring(0, 30) + "..." : c; } + private String sectionName(String s) { switch(s){case "header":return "头部";case "itemLine":return "商品行";case "discountLine":return "优惠行";case "packLine":return "打包行";case "summary":return "汇总";case "footer":return "页脚";default:return s;} } + private String sc(String s) { + if (s == null) return ""; + return s.replaceAll("§[0-9a-fk-orA-FK-OR]", ""); + } + private String getSection(String s) { switch(s){case "header":return tplHeader;case "itemLine":return tplItemLine;case "discountLine":return tplDiscountLine;case "packLine":return tplPackLine;case "summary":return tplSummary;case "footer":return tplFooter;default:return "";} } + private void setSection(String s, String v) { switch(s){case "header":tplHeader=v;break;case "itemLine":tplItemLine=v;break;case "discountLine":tplDiscountLine=v;break;case "packLine":tplPackLine=v;break;case "summary":tplSummary=v;break;case "footer":tplFooter=v;break;} } + private String getDefaultVal(String s) { switch(s){case "header":return "§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}";case "itemLine":return "§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚";case "discountLine":return "优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})";case "packLine":return "打包: §c+{打包费}枚§r ({打包颜色})";case "summary":return "原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚";case "footer":return "感谢惠顾,欢迎再来!";default:return "";} } + private String getTokenBySlot(int raw) { switch(raw){case 10:return "{品名}";case 11:return "{原价}";case 12:return "{执行价}";case 13:return "{数量}";case 14:return "{小计}";case 15:return "{商店名}";case 16:return "{玩家}";case 19:return "{订单号}";case 20:return "{订单时间}";case 21:return "{打印时间}";case 22:return "{日期}";case 23:return "{时间}";case 24:return "{原价合计}";case 25:return "{实付}";case 28:return "{优惠额}";case 29:return "{优惠类型}";case 30:return "{优惠券码}";case 31:return "{打包费}";case 32:return "{打包颜色}";case 33:return "{余额}";case 37:return "═══";case 38:return "───";case 39:return "═════════════";case 40:return "\\n";default:return null;} } + private ItemStack mkItem(Material mat, String name, String... lore) { ItemStack it = new ItemStack(mat); ItemMeta im = it.getItemMeta(); if (im != null) { im.setDisplayName(name); if (lore.length > 0) im.setLore(Arrays.asList(lore)); it.setItemMeta(im); } return it; } + private Map loadMap(File f) { Map m = new LinkedHashMap<>(); try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); String line; while ((line = br.readLine()) != null) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) continue; int eq = line.indexOf('='); if (eq > 0) m.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); } br.close(); } catch (Exception ignored) {} return m; } + private void writeLines(File f, List lines) { try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); for (String l : lines) pw.println(l); pw.flush(); pw.close(); } catch (Exception ignored) {} } + private void sendRandomPreview(Player p) { String[] n = {"钻石剑","钻石镐","铁剑","金苹果"}; String[] m = {"DIAMOND_SWORD","DIAMOND_PICKAXE","IRON_SWORD","GOLDEN_APPLE"}; Random r = new Random(); List items = new ArrayList<>(); for (int i = 0; i < 2 + r.nextInt(3); i++) { int idx = r.nextInt(n.length); items.add(new OrderItem(n[idx], m[idx], 100 + r.nextInt(5000), 100 + r.nextInt(5000), 1 + r.nextInt(10))); } OrderRecord o = new OrderRecord(); o.orderId = System.currentTimeMillis(); o.uuid = p.getUniqueId(); o.player = p.getName(); o.items = items; o.totalOriginal = 0; for (OrderItem it : items) o.totalOriginal += it.originalSubtotal(); o.totalPaid = o.totalOriginal; o.timestamp = o.orderId; o.status = 0; p.sendMessage("§6§l===== 小票预览(随机) ====="); sendReceiptChat(p, o); p.sendMessage("§6§l===== 预览结束 ====="); } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/PVPManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/PVPManager.java new file mode 100644 index 0000000..326c0fb --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/PVPManager.java @@ -0,0 +1,1380 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.scoreboard.*; + + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class PVPManager implements Listener { + + private final Main plugin; + private Connection pvpDb; + + private final Map + regions = new ConcurrentHashMap<>(); + private final Map + inRegion = new ConcurrentHashMap<>(); + private final Map + sessionKills = + new ConcurrentHashMap<>(); + private final Map + lastKillTime = + new ConcurrentHashMap<>(); + private final Map + selectState = + new ConcurrentHashMap<>(); + private final Map + killSessions = + new ConcurrentHashMap<>(); + private final Set tripleAnnounced = + ConcurrentHashMap.newKeySet(); + private final Map lastScoreboardRefresh = new ConcurrentHashMap<>(); + + + // 边框显示 + private final Set + permanentBorders = + ConcurrentHashMap.newKeySet(); + private final Set + tempBorders = + ConcurrentHashMap.newKeySet(); + + + public static class KillSession { + public int count = 0; + public long firstKill = 0; + public long lastKill = 0; + public boolean tripleAnnounced = + false; + public String region = ""; + } + + public static class PVPRegion { + public String name; + public String world; + public int x1, y1, z1; + public int x2, y2, z2; + public int minX, minY, minZ; + public int maxX, maxY, maxZ; + + public void calcBounds() { + minX = Math.min(x1, x2); + minY = Math.min(y1, y2); + minZ = Math.min(z1, z2); + maxX = Math.max(x1, x2); + maxY = Math.max(y1, y2); + maxZ = Math.max(z1, z2); + } + + public boolean contains( + Location loc) { + if (loc == null) return false; + if (!loc.getWorld().getName() + .equals(world)) + return false; + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + return x >= minX && x <= maxX + && y >= minY && y <= maxY + && z >= minZ && z <= maxZ; + } + } + + public PVPManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadRegions(); + } + + // ===== 独立数据库 ===== + + private void initDB() { + try { + Class.forName( + "org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), + "pvp.db"); + pvpDb = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = + pvpDb.createStatement(); + st.execute( + "PRAGMA journal_mode=WAL"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "name TEXT NOT NULL " + + "UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER " + + "DEFAULT 0)"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT " + + "NOT NULL," + + "region_name TEXT " + + "NOT NULL," + + "kills INTEGER " + + "DEFAULT 0," + + "deaths INTEGER " + + "DEFAULT 0," + + "PRIMARY KEY" + + "(player_name, " + + "region_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[PVP] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== DB 操作 ===== + + private void saveRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions" + + " (name, world," + + " x1,y1,z1," + + " x2,y2,z2," + + " create_time)" + + " VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Map + getRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + private List> + getAllRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + pvpDb.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + private void deleteRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private int getKills(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("kills"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private int getDeaths(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("deaths"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private void addKill(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,1,0) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET kills=" + + "kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void addDeath(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,0,1) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET deaths=" + + "deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private List> + getTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT player_name," + + " kills,deaths" + + " FROM " + + "pvp_kills " + + "WHERE " + + "region_name=?" + + " AND kills>0" + + " ORDER BY kills" + + " DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = + ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString( + "player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Set getRegionNames() { + return regions.keySet(); + } + + // ===== 加载 ===== + + private void loadRegions() { + regions.clear(); + List> list = + getAllRegions(); + for (Map row : list) { + PVPRegion r = new PVPRegion(); + r.name = (String) row + .get("name"); + r.world = (String) row + .get("world"); + r.x1 = ((Number) row + .get("x1")).intValue(); + r.y1 = ((Number) row + .get("y1")).intValue(); + r.z1 = ((Number) row + .get("z1")).intValue(); + r.x2 = ((Number) row + .get("x2")).intValue(); + r.y2 = ((Number) row + .get("y2")).intValue(); + r.z2 = ((Number) row + .get("z2")).intValue(); + r.calcBounds(); + regions.put(r.name, r); + } + } + + // ===== 工具 ===== + + public ItemStack createTool() { + ItemStack tool = new ItemStack( + Material.BLAZE_ROD); + ItemMeta m = tool.getItemMeta(); + m.setDisplayName( + "§6§l[PVP] 区域选择工具"); + m.setLore(Arrays.asList( + "§7右键:设置第一个角", + "§7左键:设置第二个角")); + m.getPersistentDataContainer() + .set(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING, + "tool"); + tool.setItemMeta(m); + return tool; + } + + private boolean isPVPTool( + ItemStack item) { + if (item == null) return false; + ItemMeta m = item.getItemMeta(); + if (m == null) return false; + return m.getPersistentDataContainer() + .has(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING); + } + + public void giveTool(Player p) { + boolean has = false; + for (ItemStack it : p.getInventory() + .getContents()) { + if (isPVPTool(it)) { + has = true; + break; + } + } + if (!has) { + p.getInventory() + .addItem(createTool()); + } + } + + // ===== 边框粒子 ===== + + private void drawBorder(Player target, + PVPRegion r) { + if (r == null) return; + World w = Bukkit.getWorld(r.world); + if (w == null) return; + int step = 2; + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.maxX, r.minY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.minY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.minX, r.minY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.minY, r.minZ, step); + drawLine(target, w, + r.minX, r.maxY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.minZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.maxY, r.maxZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + } + + private void drawLine(Player target, + World w, + int x1, int y1, int z1, + int x2, int y2, int z2, + int step) { + double dx = x2 - x1; + double dy = y2 - y1; + double dz = z2 - z1; + double dist = Math.sqrt( + dx * dx + dy * dy + dz * dz); + int steps = (int) (dist / step); + if (steps < 1) steps = 1; + for (int i = 0; i <= steps; i++) { + double t = (double) i / steps; + Location loc = new Location(w, + x1 + dx * t, + y1 + dy * t + 0.1, + z1 + dz * t); + w.spawnParticle( + Particle.FLAME, loc, 1, + 0.0, 0.0, 0.0, 0.0); + } + } + + // ===== 每秒 tick ===== + + public void tickRegions() { + long now = + System.currentTimeMillis(); + + // 玩家区域检测 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pn = p.getName(); + Location loc = p.getLocation(); + String inside = null; + for (PVPRegion r : + regions.values()) { + if (r.contains(loc)) { + inside = r.name; + break; + } + } + String was = inRegion.get(pn); + + if (inside != null && !inside.equals(was)) { + inRegion.put(pn, inside); + if (was == null) { + String reg = inside; + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> + updateScoreboard( + pp, reg), + 1L); + } + + } else if (inside == null + && was != null) { + inRegion.remove(pn); + // 不删 sessionKills,等倒计时结束再清 + removeScoreboard(p); + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> plugin + .giveMenuSnowball(pp), + 5L); + } + + } + + // 5分钟超时重置连杀session + for (String pname : + new ArrayList<>( + killSessions + .keySet())) { + KillSession ks = + killSessions.get(pname); + if (ks == null) continue; + if (now - ks.lastKill + > 5 * 60 * 1000) { + killSessions.remove(pname); + } + } + + // 5分钟无击杀 → 计数榜切总榜 + // 5分钟无击杀 → 归档并清零 + for (String rName : + new ArrayList<>( + lastKillTime + .keySet())) { + long last = lastKillTime + .getOrDefault(rName, 0L); + if (now - last > 5 * 60 * 1000) { + // 清零该区域所有sessionKills + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + rName + ":")); + // 切换到总榜 + refreshScoreboard(rName); + lastKillTime.remove(rName); + lastScoreboardRefresh.remove(rName); + + } + } + + + // 边框渲染 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (pr == null) continue; + PVPRegion r = regions.get(pr); + if (r == null) continue; + if (permanentBorders + .contains(pr)) { + drawBorder(p, r); + } + if (tempBorders + .contains(pr)) { + drawBorder(p, r); + } + // 每秒刷新活跃区域的计分板倒计时 + for (String rName : new ArrayList<>(lastKillTime.keySet())) { + long last = lastScoreboardRefresh.getOrDefault(rName, 0L); + if (now - last >= 1000) { + lastScoreboardRefresh.put(rName, now); + for (Player tp : Bukkit.getOnlinePlayers()) { + String inR = inRegion.get(tp.getName()); + if (rName.equals(inR)) { + showCounter(tp, rName); + } + } + } + } + + + + } + } + + private void broadcastWithSound(String msg, Sound sound) { + Bukkit.broadcastMessage(msg); + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), sound, 1.0f, 1.0f); + } + } + + + private void initSession(String player, + String region) { + killSessions.remove(player); + tripleAnnounced.remove( + region + ":" + player); + KillSession s = new KillSession(); + s.region = region; + s.firstKill = + System.currentTimeMillis(); + s.lastKill = + System.currentTimeMillis(); + killSessions.put(player, s); + } + + // ===== 击杀事件 ===== + + @EventHandler + public void onDeath( + EntityDeathEvent event) { + if (!(event.getEntity() + instanceof Player)) return; + Player victim = + (Player) event.getEntity(); + Player killer = + victim.getKiller(); + if (killer == null) return; + if (killer.equals(victim)) return; + + String region = + inRegion.get( + victim.getName()); + if (region == null) return; + + String vName = victim.getName(); + String kName = killer.getName(); + + // 总榜 + addKill(kName, region); + addDeath(vName, region); + + // 计数榜 + String kKey = + region + ":" + kName; + sessionKills.merge(kKey, 1, + Integer::sum); + lastKillTime.put(region, + System.currentTimeMillis()); + + // 终结检测 + // 终结检测 + KillSession victimSession = + killSessions.get(vName); + if (victimSession != null + && victimSession.count >= 3) { + String endMsg = + "§b§l" + kName + + " §e终结了 " + + vName + " §e的 " + + victimSession.count + + "连杀!§a恭喜终结者 " + + kName + + "§e!§7快来挑战!"; + killSessions.remove(vName); + broadcastWithSound(endMsg, Sound.ENTITY_ENDER_DRAGON_GROWL); + + killSessions.remove(vName); + } + + + // 击杀者连杀 + KillSession ks = + killSessions.get(kName); + if (ks == null + || !ks.region.equals(region) + || System.currentTimeMillis() + - ks.lastKill > 5 * 60 * 1000) { + tripleAnnounced.remove( + region + ":" + kName); + KillSession ns = + new KillSession(); + ns.region = region; + ns.count = 1; + ns.firstKill = + System.currentTimeMillis(); + ns.lastKill = + System.currentTimeMillis(); + killSessions.put(kName, ns); + } else { + ks.count++; + ks.lastKill = + System.currentTimeMillis(); + } + + // 多杀通报 + KillSession killerSession = + killSessions.get(kName); + if (killerSession != null) { + plugin.getLogger().info( + "[PVP] checkMultiKill: " + + kName + " count=" + + killerSession.count + + " region=" + region); + checkMultiKill(killer, + region, killerSession); + } + + // 刷新计分板 + for (Player p : + Bukkit.getOnlinePlayers()) { + if (region.equals( + inRegion.get( + p.getName()))) { + updateScoreboard(p, region); + } + } + + victim.sendMessage("§c你在「" + + region + "」被 " + + kName + " 击杀"); + killer.sendMessage("§a你在「" + + region + "」击杀了 " + + vName); + } + + private void checkMultiKill( + Player killer, String region, + KillSession session) { + int count = session.count; + String kName = killer.getName(); + String key = region + ":" + kName; + String msg = null; + + if (count >= 5) { + msg = "§c§l" + kName + + " §e在「" + region + + "」达成 " + count + + "连杀!§7§l快来挑战!"; + } else if (count == 3) { + if (tripleAnnounced.add(key)) { + msg = "§6§l三杀!" + + kName + + " 势不可挡!" + + "§7快来挑战!"; + } + } + + if (msg != null) { + if (count >= 5) { + broadcastWithSound(msg, Sound.ENTITY_ENDER_DRAGON_GROWL); + } else { + broadcastWithSound(msg, Sound.BLOCK_END_PORTAL_SPAWN); + } + } + + } + + + + // ===== 计分板(英文ID) ===== + + private void ensureScoreboard( + Player p, String region) { + // 活跃击杀:每秒强制刷新(更新倒计时) + if (lastKillTime.containsKey(region)) { + showCounter(p, region); + return; + } + // 无活跃击杀:只在没有scoreboard时创建总榜 + Scoreboard sb = p.getScoreboard(); + if (sb == null + || sb.getObjective( + "sdf1pvp") == null) { + showTotal(p, region); + } + } + + + private void updateScoreboard( + Player p, String region) { + if (lastKillTime + .containsKey(region)) { + showCounter(p, region); + } else { + showTotal(p, region); + } + } + + private void showTotal(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = + mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§6§l击杀总榜 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List> top = + getTop(region, 10); + if (top.isEmpty()) { + obj.getScore("§7暂无记录") + .setScore(0); + } else { + int rank = 10; + for (Map row : + top) { + String name = (String) row + .get("player_name"); + int kills = ((Number) row + .get("kills")) + .intValue(); + int deaths = ((Number) row + .get("deaths")) + .intValue(); + String line = + "§e#" + (11 - rank) + + " §f" + name + + " §a" + kills + + "§7/§c" + deaths; + obj.getScore(line) + .setScore(rank); + rank--; + } + } + p.setScoreboard(sb); + } + private void showCounter(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§c§l实时战况 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List entries = + new ArrayList<>(); + for (Map.Entry e : + sessionKills.entrySet()) { + if (!e.getKey().startsWith( + region + ":")) + continue; + if (e.getValue() <= 0) continue; + String pName = e.getKey() + .substring( + region.length() + 1); + entries.add(new String[]{ + pName, + String.valueOf( + e.getValue())}); + } + + if (entries.isEmpty()) { + obj.getScore("§7等待击杀...") + .setScore(1); + } else { + entries.sort((a, b) -> + Integer.compare( + Integer.parseInt(b[1]), + Integer.parseInt(a[1]))); + int rank = 10; + for (String[] entry : entries) { + obj.getScore( + "§e" + entry[0] + + " §a" + entry[1]) + .setScore(rank); + rank--; + if (rank < 1) break; + } + } + + Long last = + lastKillTime.get(region); + if (last != null) { + long elapsed = + System.currentTimeMillis() + - last; + long remaining = + Math.max(0, + 5 * 60 * 1000 - elapsed); + long secs = remaining / 1000; + obj.getScore( + "§7冷却: " + secs + "s") + .setScore(-1); + } + + // 关键:强制赋值新scoreboard + p.setScoreboard(sb); + } + + private void removeScoreboard( + Player p) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + p.setScoreboard( + mgr.getNewScoreboard()); + } + + private void refreshScoreboard( + String regionName) { + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (regionName.equals(pr)) { + showTotal(p, regionName); + } + } + } + + // ===== 工具选择事件 ===== + + @EventHandler + public void onInteract( + PlayerInteractEvent event) { + Player p = event.getPlayer(); + ItemStack hand = + p.getInventory() + .getItemInMainHand(); + if (!isPVPTool(hand)) return; + + if (!event.getAction() + .isRightClick() + && !event.getAction() + .isLeftClick()) return; + + Location loc = p.getLocation(); + String[] state = + selectState.get(p.getName()); + if (state == null) { + state = new String[]{ + loc.getWorld() + .getName(), + "", ""}; + selectState.put( + p.getName(), state); + } + + String coord = + loc.getBlockX() + "," + + loc.getBlockY() + "," + + loc.getBlockZ(); + + if (event.getAction() + .isRightClick()) { + state[0] = loc.getWorld() + .getName(); + state[1] = coord; + p.sendMessage( + "§a第一个角: " + coord); + } else { + state[2] = coord; + p.sendMessage( + "§a第二个角: " + coord); + } + event.setCancelled(true); + } + + // ===== 退出清理 ===== + + @EventHandler + public void onQuit( + PlayerQuitEvent event) { + String name = + event.getPlayer().getName(); + inRegion.remove(name); + selectState.remove(name); + } + + // ===== 指令 ===== + + public boolean onCommand(Player p, + String[] args) { + if (args.length == 0) { + return showStats(p, + p.getName()); + } + switch (args[0].toLowerCase()) { + case "create": + return handleCreate( + p, args); + case "stats": + if (args.length >= 2) + return showStats(p, + args[1]); + return showStats(p, + p.getName()); + case "list": + return handleList(p); + case "delete": + if (args.length >= 2) + return handleDelete(p, + args[1]); + p.sendMessage( + "§c用法: " + + "/pvp delete <名字>"); + return true; + case "tool": + giveTool(p); + p.sendMessage( + "§a已发放" + + "PVP区域选择工具"); + return true; + case "on": + return handleBorderOn( + p, args); + case "off": + return handleBorderOff( + p, args); + case "tempban": + return handleTempBan( + p, args); + default: + return showStats(p, + p.getName()); + } + } + + private boolean handleBorderOn( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp on <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + permanentBorders.add(name); + p.sendMessage("§a已开启「" + + name + "」永久边框显示"); + return true; + } + + private boolean handleBorderOff( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp off <区域名>"); + return true; + } + permanentBorders.remove(name); + tempBorders.remove(name); + p.sendMessage("§a已关闭「" + + name + "」边框显示"); + return true; + } + + private boolean handleTempBan( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp tempban <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + tempBorders.add(name); + p.sendMessage("§a「" + name + + "」临时边框显示15秒"); + final String rName = name; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> + tempBorders + .remove(rName), + 300L); + return true; + } + + private boolean handleCreate( + Player p, String[] args) { + if (args.length >= 8) { + try { + int x1 = Integer.parseInt( + args[1]); + int y1 = Integer.parseInt( + args[2]); + int z1 = Integer.parseInt( + args[3]); + int x2 = Integer.parseInt( + args[4]); + int y2 = Integer.parseInt( + args[5]); + int z2 = Integer.parseInt( + args[6]); + String rName = args[7]; + return createRegion(p, + rName, + p.getWorld() + .getName(), + x1, y1, z1, + x2, y2, z2); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c坐标格式错误"); + return true; + } + } + + if (args.length >= 2) { + String[] state = + selectState + .get(p.getName()); + if (state == null + || state[1].isEmpty() + || state[2].isEmpty()) { + p.sendMessage( + "§c请先用工具选择两个角"); + return true; + } + String[] p1 = + state[1].split(","); + String[] p2 = + state[2].split(","); + return createRegion(p, + args[1], state[0], + Integer.parseInt(p1[0]), + Integer.parseInt(p1[1]), + Integer.parseInt(p1[2]), + Integer.parseInt(p2[0]), + Integer.parseInt(p2[1]), + Integer.parseInt(p2[2])); + } + + p.sendMessage( + "§c用法: /pvp create " + + " " + + " <名字>"); + p.sendMessage( + "§c或: /pvp create " + + "<名字> (先用工具选择)"); + return true; + } + + private boolean createRegion( + Player p, String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + if (getRegion(name) != null) { + p.sendMessage( + "§c区域已存在: " + name); + return true; + } + PVPRegion r = new PVPRegion(); + r.name = name; + r.world = world; + r.x1 = x1; + r.y1 = y1; + r.z1 = z1; + r.x2 = x2; + r.y2 = y2; + r.z2 = z2; + r.calcBounds(); + saveRegion(name, world, + x1, y1, z1, + x2, y2, z2); + regions.put(name, r); + p.sendMessage( + "§a§lPVP区域创建成功!"); + p.sendMessage( + "§7名称: §f" + name); + p.sendMessage("§7范围: §f" + + r.minX + "," + r.minY + + "," + r.minZ + " → " + + r.maxX + "," + r.maxY + + "," + r.maxZ); + selectState.remove(p.getName()); + return true; + } + + private boolean handleList( + Player p) { + List> all = + getAllRegions(); + if (all.isEmpty()) { + p.sendMessage( + "§7暂无PVP区域"); + return true; + } + p.sendMessage( + "§6§lPVP区域列表:"); + for (Map row : + all) { + p.sendMessage("§e- §f" + + row.get("name") + + " §7(" + row.get("world") + + ")"); + } + return true; + } + + private boolean handleDelete( + Player p, String name) { + if (getRegion(name) == null) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + deleteRegion(name); + regions.remove(name); + permanentBorders.remove(name); + tempBorders.remove(name); + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + name + ":")); + lastKillTime.remove(name); + lastScoreboardRefresh.remove(name); + p.sendMessage( + "§a已删除: " + name); + return true; + } + + private boolean showStats(Player p, + String target) { + String region = + inRegion.get(target); + if (region == null) { + p.sendMessage("§6§l" + target + + " 的PVP战绩:"); + List> all = + getAllRegions(); + boolean has = false; + for (Map row : + all) { + String rn = (String) row + .get("name"); + int kills = + getKills(target, rn); + int deaths = + getDeaths(target, rn); + if (kills > 0 + || deaths > 0) { + p.sendMessage( + "§e- §f" + rn + + " §a击杀:" + kills + + " §c死亡:" + deaths); + has = true; + } + } + if (!has) + p.sendMessage( + "§7暂无记录"); + } else { + int kills = + getKills(target, region); + int deaths = + getDeaths(target, region); + double kd = deaths > 0 + ? (double) kills / deaths + : kills; + p.sendMessage("§6§l" + target + + " @ " + region); + p.sendMessage( + "§a击杀: " + kills + + " §c死亡: " + deaths + + " §eKD: " + + String.format( + "%.2f", kd)); + } + return true; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/PasswordUtils.java b/backup/20260605_152529/src/main/java/Sdf1_login/PasswordUtils.java new file mode 100644 index 0000000..4029ed4 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/PasswordUtils.java @@ -0,0 +1,72 @@ +package Sdf1_login; + +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Base64; + +public class PasswordUtils { + + private static final SecureRandom RANDOM = + new SecureRandom(); + + public static String generateSalt() { + byte[] salt = new byte[16]; + RANDOM.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + public static String hash(String password, String salt) { + try { + MessageDigest md = + MessageDigest.getInstance("SHA-256"); + md.update(Base64.getDecoder().decode(salt)); + byte[] hash = + md.digest(password.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(hash); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean validate(String password) { + if (password == null) return false; + if (password.length() < 6 || password.length() > 20) + return false; + boolean hasUpper = false; + boolean hasLower = false; + boolean hasDigit = false; + for (char c : password.toCharArray()) { + if (Character.isUpperCase(c)) hasUpper = true; + if (Character.isLowerCase(c)) hasLower = true; + if (Character.isDigit(c)) hasDigit = true; + } + return hasUpper && hasLower && hasDigit; + } + + public static String generateTempPassword() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(8); + for (int i = 0; i < 8; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateInviteCode() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(6); + for (int i = 0; i < 6; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateVerifyCode() { + int code = 100000 + RANDOM.nextInt(900000); + return String.valueOf(code); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/PlayerStorage.java b/backup/20260605_152529/src/main/java/Sdf1_login/PlayerStorage.java new file mode 100644 index 0000000..f93caa3 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/PlayerStorage.java @@ -0,0 +1,130 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.logging.Logger; + +public class PlayerStorage { + private Connection conn; + private final Logger logger; + + public PlayerStorage(File dataFolder, + Logger logger) { + this.logger = logger; + File dbFile = new File( + dataFolder, + "quest_data.db"); + try { + Class.forName( + "org.sqlite.JDBC"); + conn = DriverManager + .getConnection( + "jdbc:sqlite:" + + dbFile + .getAbsolutePath()); + conn.setAutoCommit(true); + Statement s = + conn.createStatement(); + s.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_kv (" + + "player TEXT NOT NULL," + + "key TEXT NOT NULL," + + "value TEXT," + + "PRIMARY KEY" + + "(player, key))"); + s.close(); + logger.info( + "[QuestStorage] 初始化: " + + dbFile.getAbsolutePath()); + } catch (Exception e) { + logger.severe( + "[QuestStorage] 失败: " + + e.getMessage()); + } + } + + public Object getCondition( + String player, String key) { + if (conn == null) return null; + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value " + + "FROM quest_kv " + + "WHERE player=? " + + "AND key=?"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ResultSet rs = + ps.executeQuery(); + String val = rs.next() + ? rs.getString("value") + : null; + rs.close(); + ps.close(); + return parseValue(val); + } catch (SQLException e) { + return null; + } + } + + public void setCondition( + String player, String key) { + setConditionRaw( + player, key, "true"); + } + public void debugPlayer(String player) { + + } + public void setConditionRaw( + String player, String key, + String value) { + if (conn == null) return; + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO quest_kv" + + "(player,key,value)" + + " VALUES(?,?,?)"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ps.setString(3, value); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + logger.warning( + "[QuestStorage] 写入失败: " + + key + "=" + value + + " | " + e.getMessage()); + } + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) + conn.close(); + } catch (Exception e) {} + } + + private Object parseValue(String v) { + if (v == null) return null; + try { + return Long.parseLong(v); + } catch (Exception e1) { + try { + return Double.parseDouble(v); + } catch (Exception e2) { + if ("true".equalsIgnoreCase(v)) + return true; + if ("false".equalsIgnoreCase(v)) + return false; + return v; + } + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/PointsManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/PointsManager.java new file mode 100644 index 0000000..77fc195 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/PointsManager.java @@ -0,0 +1,374 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + + +import java.lang.reflect.Method; +import java.util.*; + +public class PointsManager { + + private final Main plugin; + private Object cyInstance = null; + private Method cyActivationMethod = null; + private Method cyGetShopItemsMethod = null; + + private final List> cyItems = + new ArrayList<>(); + + public PointsManager(Main plugin) { + this.plugin = plugin; + discoverCY(); + } + + private void discoverCY() { + try { + org.bukkit.plugin.Plugin p = Bukkit + .getPluginManager() + .getPlugin("CY_beibao"); + if (p != null && p.isEnabled()) { + cyInstance = p; + cyGetShopItemsMethod = p.getClass() + .getMethod("onSdf1GetShopItems"); + cyActivationMethod = p.getClass() + .getMethod("onSdf1Activation", + String.class, + int.class, int.class); + plugin.getLogger().info( + "[Sdf1_login] CY_beibao 联控已连接"); + } + } catch (Exception ignored) { + } + } + + private void refreshCYItems() { + cyItems.clear(); + if (cyInstance == null + || cyGetShopItemsMethod == null) return; + try { + Object result = + cyGetShopItemsMethod.invoke(cyInstance); + if (result == null) return; + String data = result.toString(); + if (data.isEmpty()) return; + String[] items = data.split(";"); + for (String itemStr : items) { + String[] p = itemStr.split("\\|"); + if (p.length < 8) continue; + Map item = + new LinkedHashMap<>(); + item.put("id", p[0]); + item.put("name", p[1]); + item.put("days", + Integer.parseInt(p[2])); + item.put("slots", + Integer.parseInt(p[3])); + item.put("price", + Integer.parseInt(p[4])); + item.put("stock", + Integer.parseInt(p[5])); + item.put("icon", p[6]); + item.put("lifetime", + "1".equals(p[7])); + cyItems.add(item); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 拉取CY商品失败: " + + e.getMessage()); + } + } + + private void activateCY(String name, + int slots, int days) { + try { + if (cyInstance == null + || cyActivationMethod == null) return; + cyActivationMethod.invoke( + cyInstance, name, slots, days); + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] CY激活失败: " + + e.getMessage()); + } + } + + public int getPoints(String name) { + return ((Number) plugin.getDb() + .getField(name, "points")).intValue(); + } + + public Inventory createShopGUI(Player p) { + int points = getPoints(p.getName()); + Inventory inv = Bukkit.createInventory( + null, 54, + "§d§l积分商城 | 当前积分: " + points); + fillBg(inv); + inv.setItem(10, mkItem(Material.EMERALD, + "§a游戏币充值", + "§7消耗积分兑换游戏币", + "§7100积分 = $1000", + "", + "§e点击兑换")); + inv.setItem(12, mkItem(Material.ENDER_CHEST, + "§b背包空间", + "§7消耗积分兑换背包格子", + "§750积分 = 9格永久空间", + "", + "§e点击兑换")); + inv.setItem(14, mkItem(Material.EXPERIENCE_BOTTLE, + "§e经验瓶", + "§7消耗积分兑换经验", + "§720积分 = 10级经验", + "", + "§e点击兑换")); + if (cyInstance != null) { + inv.setItem(31, mkItem(Material.CHEST, + "§6背包商城", + "§7来自CY_beibao的商品", + "", + "§e点击查看")); + } + inv.setItem(49, mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public Inventory createCYShopGUI(Player p) { + refreshCYItems(); + int points = getPoints(p.getName()); + int size = Math.max(54, + ((cyItems.size() / 9) + 1) * 9); + if (size > 54) size = 54; + Inventory inv = Bukkit.createInventory( + null, size, + "§d§lCY背包商城 | 当前积分: " + points); + fillBg(inv); + for (int i = 0; i < cyItems.size() + && i < 45; i++) { + Map item = cyItems.get(i); + String name = (String) item.get("name"); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + List lore = new ArrayList<>(); + lore.add("§7价格: §e" + price + " 积分"); + if (lifetime) { + lore.add("§7格子: §e终身 +" + slots); + } else { + lore.add("§7格子: §e+" + slots); + lore.add("§7时间: §e" + days + "天"); + } + if (stock == 0) { + lore.add("§7库存: §a无限"); + } else if (stock == -1) { + lore.add("§7库存: §c已下架"); + } else { + lore.add("§7库存: §e" + stock); + } + lore.add(""); + if (points >= price && stock != -1) { + lore.add("§e点击购买"); + } else if (stock == -1) { + lore.add("§c已下架"); + } else { + lore.add("§c积分不足"); + } + inv.setItem(i, mkItem(Material.CHEST, + "§a" + name, + lore.toArray(new String[0]))); + } + inv.setItem(size - 1, + mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public void addItem(String name, + int price, String cmd2) { + // 追加到商品配置文件 + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.io.FileWriter fw = + new java.io.FileWriter(file, true); + fw.write(name + ":" + price + + ":" + cmd2 + "\n"); + fw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void removeItem(int index) { + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.util.List lines = + new java.util.ArrayList<>(); + java.io.BufferedReader br = + new java.io.BufferedReader( + new java.io.FileReader( + file)); + String line; + int i = 0; + while ((line = br.readLine()) != null) { + if (line.trim().isEmpty() + || line.startsWith("#")) { + lines.add(line); + continue; + } + if (i != index) { + lines.add(line); + } + i++; + } + br.close(); + java.io.PrintWriter pw = + new java.io.PrintWriter(file); + for (String l : lines) pw.println(l); + pw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void handleClick(Player p, int slot) { + int points = getPoints(p.getName()); + switch (slot) { + case 10: + if (points >= 100) { + plugin.getDb().deductPoints( + p.getName(), 100); + if (plugin.getEconomy() != null) { + plugin.getEconomy() + .depositPlayer(p, 1000); + } + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "100")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 12: + if (points >= 50) { + plugin.getDb().deductPoints( + p.getName(), 50); + activateCY(p.getName(), 9, 0); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "50")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 14: + if (points >= 20) { + plugin.getDb().deductPoints( + p.getName(), 20); + p.setLevel(p.getLevel() + 10); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "20")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 31: + if (cyInstance != null) { + p.openInventory( + createCYShopGUI(p)); + } + break; + } + } + + public void handleCYClick(Player p, int slot) { + if (slot < 0 || slot >= cyItems.size() + || slot >= 45) return; + Map item = cyItems.get(slot); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + if (stock == -1) { + p.sendMessage("§c该商品已下架"); + return; + } + int points = getPoints(p.getName()); + if (points < price) { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + return; + } + + plugin.getDb().deductPoints(p.getName(), price); + int actDays = lifetime ? 0 : days; + activateCY(p.getName(), slots, actDays); + + String tl = lifetime + ? "终身" : (days + "天"); + p.sendMessage("§a购买成功!获得 " + + tl + " +" + slots + "格 背包空间"); + } + + private void fillBg(Inventory inv) { + ItemStack gl = mkItem( + Material.GRAY_STAINED_GLASS_PANE, " "); + for (int i = 0; i < inv.getSize(); i++) + inv.setItem(i, gl); + } + + private ItemStack mkItem(Material mat, String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/QuestStorage.java b/backup/20260605_152529/src/main/java/Sdf1_login/QuestStorage.java new file mode 100644 index 0000000..45dbabf --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/QuestStorage.java @@ -0,0 +1,138 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class QuestStorage { + + private Connection conn; + + public QuestStorage(File dataFolder) { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + dataFolder, + "quest_data.db"); + conn = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getPath()); + initTable(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void initTable() + throws SQLException { + Statement stmt = + conn.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_conditions (" + + "player TEXT NOT NULL, " + + "condition_key TEXT NOT NULL, " + + "value TEXT DEFAULT '1', " + + "timestamp INTEGER, " + + "PRIMARY KEY " + + "(player, condition_key))"); + stmt.close(); + } + + /** + * 标记条件已完成(value = "1") + */ + public void setCondition( + String player, + String condition) { + setConditionRaw( + player, condition, "1"); + } + + /** + * 写入任意值(计数器等) + */ + public void setConditionRaw( + String player, + String condition, + String value) { + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO " + + "quest_conditions " + + "(player, " + + "condition_key, " + + "value, timestamp) " + + "VALUES " + + "(?, ?, ?, ?)"); + ps.setString(1, player); + ps.setString(2, condition); + ps.setString(3, value); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 查询条件值 + */ + public Object getCondition( + String player, + String condition) { + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value FROM " + + "quest_conditions " + + "WHERE player = ? " + + "AND " + + "condition_key = ?"); + ps.setString(1, player); + ps.setString(2, condition); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + String val = + rs.getString("value"); + rs.close(); + ps.close(); + // 尝试解析为数字 + try { + if (val.contains(".")) { + return Double.parseDouble( + val); + } + return Long.parseLong(val); + } catch (NumberFormatException e) { + return val; + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/QuestTracker.java b/backup/20260605_152529/src/main/java/Sdf1_login/QuestTracker.java new file mode 100644 index 0000000..f2eda67 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/QuestTracker.java @@ -0,0 +1,3662 @@ +package Sdf1_login; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.generator.structure.Structure; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +// 如果 Bonds 在 Sdf1_login 包下: +import Sdf1_login.BondManager; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class QuestTracker { + + private final Plugin plugin; + private final QuestStorage storage; + private final Map + playerBiome = new HashMap<>(); + private final Map> + questCache = new HashMap<>(); + private final List + checkInRecords = new ArrayList<>(); + private final Map + lastCheckInPos = new HashMap<>(); + private static boolean loggedStructureRegistry + = false; + + public void recordCheckIn( + String player, + String biomeKey, + String category, + String worldName, + double x, double y, double z) { + String record = player + + "|" + worldName + + "|" + biomeKey + + "|" + category + + "|" + String.format( + "%.1f", x) + + "|" + String.format( + "%.1f", y) + + "|" + String.format( + "%.1f", z) + + "|" + System.currentTimeMillis(); + checkInRecords.add(record); + plugin.getLogger().info( + "[QuestTracker] 打卡记录: " + + record); + } + + public List getCheckInRecords() { + return new ArrayList<>(checkInRecords); + } + + public List getPlayerRecords( + String player) { + List list = new ArrayList<>(); + for (String r : checkInRecords) { + if (r.startsWith( + player.toLowerCase() + + "|")) { + list.add(r); + } + } + return list; + } + + + public QuestTracker(Plugin plugin) { + this.plugin = plugin; + this.storage = new QuestStorage( + plugin.getDataFolder()); + + loadAllQuests(); + } + + public void debugPlayer(String player) { + plugin.getLogger().info( + "[QuestTracker] === " + + player + " 任务数据 ==="); + } + public void shutdown() { + if (storage != null) { + storage.close(); + } + } + + /** + * 玩家移动时记录当前群系 + */ + public void onPlayerMove( + String player, + String biomeName) { + playerBiome.put(player, biomeName); + } + /** + * 标记玩家已打卡该群系 + */ + // ========== 群系分类映射表 ========== + private static final Map + BIOME_CATEGORY = new HashMap<>(); + static { + // 海洋群系 + BIOME_CATEGORY.put("minecraft:ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:frozen_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_frozen_ocean", "海洋"); + // 平原群系 + BIOME_CATEGORY.put("minecraft:plains", "平原"); + BIOME_CATEGORY.put("minecraft:sunflower_plains", "平原"); + BIOME_CATEGORY.put("minecraft:meadow", "平原"); + // 森林群系 + BIOME_CATEGORY.put("minecraft:forest", "森林"); + BIOME_CATEGORY.put("minecraft:flower_forest", "森林"); + BIOME_CATEGORY.put("minecraft:birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:dark_forest", "森林"); + BIOME_CATEGORY.put("minecraft:taiga", "森林"); + BIOME_CATEGORY.put("minecraft:snowy_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_pine_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_spruce_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:cherry_grove", "森林"); + BIOME_CATEGORY.put("minecraft:jungle", "森林"); + BIOME_CATEGORY.put("minecraft:sparse_jungle", "森林"); + BIOME_CATEGORY.put("minecraft:bamboo_jungle", "森林"); + // 沙漠群系 + BIOME_CATEGORY.put("minecraft:desert", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna_plateau", "沙漠"); + BIOME_CATEGORY.put("minecraft:beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:snowy_beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:windswept_savanna", "沙漠"); + // 恶地群系 + BIOME_CATEGORY.put("minecraft:badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:wooded_badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:eroded_badlands", "恶地"); + // 雪原群系 + BIOME_CATEGORY.put("minecraft:snowy_plains", "雪原"); + BIOME_CATEGORY.put("minecraft:grove", "雪原"); + BIOME_CATEGORY.put("minecraft:ice_spikes", "雪原"); + BIOME_CATEGORY.put("minecraft:jagged_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_river", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:stony_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:snowy_slopes", "雪原"); + // 沙砾群系 + BIOME_CATEGORY.put("minecraft:windswept_hills", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_forest", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_gravelly_hills", "沙砾"); + // 沼泽群系 + BIOME_CATEGORY.put("minecraft:swamp", "沼泽"); + BIOME_CATEGORY.put("minecraft:mangrove_swamp", "沼泽"); + // 河流 + BIOME_CATEGORY.put("minecraft:river", "河流"); + // 蘑菇岛 + BIOME_CATEGORY.put("minecraft:mushroom_fields", "蘑菇岛"); + // 地下群系 + BIOME_CATEGORY.put("minecraft:dripstone_caves", "地下"); + BIOME_CATEGORY.put("minecraft:lush_caves", "地下"); + BIOME_CATEGORY.put("minecraft:deep_dark", "地下"); + // 下界群系 + BIOME_CATEGORY.put("minecraft:nether_wastes", "下界"); + BIOME_CATEGORY.put("minecraft:crimson_forest", "下界"); + BIOME_CATEGORY.put("minecraft:warped_forest", "下界"); + BIOME_CATEGORY.put("minecraft:soul_sand_valley", "下界"); + BIOME_CATEGORY.put("minecraft:basalt_deltas", "下界"); + // 岩岸/沙滩 + BIOME_CATEGORY.put("minecraft:stony_shore", + "石岸"); +// 海岸 + BIOME_CATEGORY.put("minecraft:mushroom_fields", + "蘑菇岛"); +// 苍白之园 + BIOME_CATEGORY.put( + "minecraft:pale_garden", "森林"); +// 末地小型岛屿 + BIOME_CATEGORY.put( + "minecraft:small_end_islands", "末地"); +// 主世界结构归类 + BIOME_CATEGORY.put("minecraft:village", "平原"); + BIOME_CATEGORY.put( + "minecraft:desert_pyramid", "沙漠"); + BIOME_CATEGORY.put( + "minecraft:jungle_pyramid", "森林"); + BIOME_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + + // 末地群系 + BIOME_CATEGORY.put("minecraft:the_end", "末地"); + BIOME_CATEGORY.put("minecraft:the_void", "末地"); + BIOME_CATEGORY.put("minecraft:end_highlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_midlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_barrens", "末地"); + + + } + + // 所有群系大类列表 + private static final String[] + ALL_CATEGORIES = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沙砾", "沼泽", + "河流", "石岸", "蘑菇岛", "地下", + "下界", "末地" + }; + + // ===== 结构映射(PaperMC 1.21.4 实际结构 key)===== + private static final Map + STRUCTURE_CATEGORY = + new HashMap<>(); + static { + // 主世界结构 + STRUCTURE_CATEGORY.put( + "minecraft:village_plains", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_desert", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_savanna", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_snowy", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_taiga", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:desert_pyramid", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:jungle_pyramid", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + STRUCTURE_CATEGORY.put( + "minecraft:igloo", "雪原"); + STRUCTURE_CATEGORY.put( + "minecraft:mansion", "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:pillager_outpost", + "掠夺者前哨站"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft_mesa", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:trail_ruins", + "远古遗迹"); + STRUCTURE_CATEGORY.put( + "minecraft:trial_chambers", + "试炼密室"); + STRUCTURE_CATEGORY.put( + "minecraft:monument", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_cold", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_warm", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck_beached", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:buried_treasure", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_desert", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_jungle", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_mountain", + "山地"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_ocean", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_swamp", + "沼泽"); + // 下界结构 + STRUCTURE_CATEGORY.put( + "minecraft:fortress", "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:bastion_remnant", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:nether_fossil", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_nether", + "下界"); + // 末地结构 + STRUCTURE_CATEGORY.put( + "minecraft:end_city", "末地"); + } + + // 所有群系分类的条件关键词映射 + private static final Map + CATEGORY_KEYWORDS = new HashMap<>(); + static { + CATEGORY_KEYWORDS.put("海洋", "海洋"); + CATEGORY_KEYWORDS.put("平原", "平原"); + CATEGORY_KEYWORDS.put("森林", "森林"); + CATEGORY_KEYWORDS.put("沙漠", "沙漠"); + CATEGORY_KEYWORDS.put("恶地", "恶地"); + CATEGORY_KEYWORDS.put("雪原", "雪原"); + CATEGORY_KEYWORDS.put("沙砾", "沙砾"); + CATEGORY_KEYWORDS.put("沼泽", "沼泽"); + CATEGORY_KEYWORDS.put("河流", "河流"); + CATEGORY_KEYWORDS.put("蘑菇岛", "蘑菇岛"); + CATEGORY_KEYWORDS.put("地下", "地下"); + CATEGORY_KEYWORDS.put("下界", "下界"); + CATEGORY_KEYWORDS.put("末地", "末地"); + // 条件文本中的别名映射 + CATEGORY_KEYWORDS.put("地狱", "下界"); + CATEGORY_KEYWORDS.put("下届", "下界"); + CATEGORY_KEYWORDS.put("主岛", "末地"); + CATEGORY_KEYWORDS.put("石岸", "石岸"); + CATEGORY_KEYWORDS.put("沙滩", "石岸"); + CATEGORY_KEYWORDS.put("海岸", "石岸"); + + // ===== 结构名称关键词映射 ===== + // 主世界结构 + CATEGORY_KEYWORDS.put("村庄", "村庄"); + CATEGORY_KEYWORDS.put("沙漠神殿", "沙漠"); + CATEGORY_KEYWORDS.put("丛林神庙", "森林"); + CATEGORY_KEYWORDS.put("沼泽小屋", "沼泽"); + CATEGORY_KEYWORDS.put("雪屋", "雪原"); + CATEGORY_KEYWORDS.put("林地府邸", "森林"); + CATEGORY_KEYWORDS.put("掠夺者前哨站", "平原"); + CATEGORY_KEYWORDS.put("废弃矿井", "地下"); + CATEGORY_KEYWORDS.put("要塞", "地下"); + CATEGORY_KEYWORDS.put("远古城市", "地下"); + CATEGORY_KEYWORDS.put("古迹废墟", "地下"); + CATEGORY_KEYWORDS.put("试炼密室", "地下"); + CATEGORY_KEYWORDS.put("海底废墟", "海洋"); + CATEGORY_KEYWORDS.put("沉船", "海洋"); + CATEGORY_KEYWORDS.put("埋藏的宝箱", "海洋"); + CATEGORY_KEYWORDS.put("废弃传送门", "主世界"); + CATEGORY_KEYWORDS.put("化石", "主世界"); + + // 下界结构 + CATEGORY_KEYWORDS.put("下界要塞", "下界"); + CATEGORY_KEYWORDS.put("猪灵堡垒", "下界"); + CATEGORY_KEYWORDS.put("玄武岩结构", "下界"); + + // 末地结构 + CATEGORY_KEYWORDS.put("末地城", "末地"); + CATEGORY_KEYWORDS.put("末地船", "末地"); + + // 下界相关别名 + CATEGORY_KEYWORDS.put("地狱要塞", "下界"); + CATEGORY_KEYWORDS.put("下界堡垒", "下界"); + + } + + /** + * 用世界维度名分类(最可靠) + * 世界名含 the_nether → 下界 + * 世界名含 the_end → 末地 + * 其余 → 主世界 + */ + public String mapBiomeCategory( + String rawKey, String worldName) { + // 优先用世界名判断维度 + if (worldName != null) { + String w = + worldName.toLowerCase(); + if (w.contains("nether")) + return "下界"; + if (w.contains("end")) + return "末地"; + } + + // 用群系映射表 + if (rawKey != null) { + String lower = + rawKey.toLowerCase(); + // 带 minecraft: 前缀的直接查 + String cat = + BIOME_CATEGORY.get(lower); + if (cat != null) + return cat; + + // 不带前缀的尝试补全 + String prefixed = + "minecraft:" + lower; + cat = BIOME_CATEGORY.get(prefixed); + if (cat != null) + return cat; + + // 模糊匹配群系名 + for (Map.Entry entry + : BIOME_CATEGORY.entrySet()) { + if (entry.getKey() + .contains(lower) + || lower.contains( + entry.getKey() + .replace( + "minecraft:", + ""))) { + return entry.getValue(); + } + } + } + + return "主世界"; + } + + // ========== 结构检测 ========== + + /** + * 检测指定位置是否存在自然生成的结构 + * 返回结构分类(如"村庄"、"沙漠"等),未找到返回null + * 使用 PaperMC World.hasStructureAt() + Registry.STRUCTURE API + */ + private String detectStructureAt( + String worldName, double x, + double y, double z) { + try { + World w = Bukkit.getWorld(worldName); + if (w == null) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测: 世界 '" + + worldName + "' 不存在"); + return null; + } + + int bx = (int) Math.floor(x); + int by = (int) Math.floor(y); + int bz = (int) Math.floor(z); + + // 首次调用时打印所有注册表结构键 + if (!loggedStructureRegistry) { + plugin.getLogger().info( + "[QuestTracker] === 结构注册表诊断 ==="); + int count = 0; + for (Structure s : Registry.STRUCTURE) { + NamespacedKey key = + Registry.STRUCTURE.getKey(s); + plugin.getLogger().info( + "[QuestTracker] 注册表结构 #" + + count + ": " + key); + count++; + } + plugin.getLogger().info( + "[QuestTracker] 注册表共 " + + count + " 个结构"); + plugin.getLogger().info( + "[QuestTracker] === 诊断结束 ==="); + loggedStructureRegistry = true; + } + + // 第1步: 精确位置检查 + io.papermc.paper.math.Position exactPos = + io.papermc.paper.math.Position.block( + bx, by, bz); + for (Structure structure : Registry.STRUCTURE) { + boolean hasAt = + w.hasStructureAt(exactPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY.get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(精确): " + + keyStr + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + + // 第2步: Y轴偏移检查 (-5 到 +10) + for (int dy = -5; dy <= 10; dy++) { + if (dy == 0) continue; + int testY = by + dy; + io.papermc.paper.math.Position testPos = + io.papermc.paper.math.Position.block( + bx, testY, bz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + testPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(Y偏移" + + dy + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + } + + // 第3步: 水平偏移检查 (±1格) + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (dx == 0 && dz == 0) continue; + io.papermc.paper.math.Position offsetPos = + io.papermc.paper.math.Position + .block(bx + dx, by, + bz + dz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + offsetPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey( + structure); + String keyStr = (key != null) + ? key.toString() + : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(偏移" + + dx + "," + + dz + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat + : "未映射")); + if (cat != null) return cat; + } + } + } + } + + plugin.getLogger().info( + "[QuestTracker] 所有位置检查完毕, " + + "未匹配任何结构 (pos=" + + bx + "," + by + "," + bz + + ")"); + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测异常: " + + ex.getClass().getSimpleName() + + ": " + ex.getMessage()); + ex.printStackTrace(); + } + return null; + } + + /** + * 将结构中文名映射到对应的大类 + * 用于匹配任务条件中的结构名 + */ + private String mapStructureCategory( + String structureName) { + if (structureName == null) return ""; + + // 主世界结构 + if (structureName.contains("村庄")) + return "村庄"; + if (structureName.contains("沙漠神殿")) + return "沙漠"; + if (structureName.contains("丛林神庙") || structureName.contains("丛林神殿")) + return "森林"; + if (structureName.contains("沼泽小屋")) + return "沼泽"; + if (structureName.contains("雪屋") || structureName.contains("冰屋")) + return "雪原"; + if (structureName.contains("林地府邸")) + return "森林"; + if (structureName.contains("掠夺者前哨站")) + return "掠夺者前哨站"; + if (structureName.contains("废弃矿井") || structureName.contains("恶地废弃矿井")) + return "地下"; + if (structureName.contains("要塞")) + return "地下"; + if (structureName.contains("远古城市")) + return "地下"; + if (structureName.contains("远古遗迹") || structureName.contains("古迹废墟")) + return "远古遗迹"; + if (structureName.contains("试炼密室")) + return "试炼密室"; + if (structureName.contains("海底神殿") || structureName.contains("海洋神殿")) + return "海洋"; + if (structureName.contains("海底废墟") || structureName.contains("冻洋废墟") || structureName.contains("暖洋废墟")) + return "海洋"; + if (structureName.contains("沉船")) + return "海洋"; + if (structureName.contains("埋藏的宝箱") || structureName.contains("埋藏的宝藏")) + return "海洋"; + if (structureName.contains("废弃传送门")) + return "平原"; + if (structureName.contains("化石")) + return "主世界"; + + // 下界结构 + if (structureName.contains("下界要塞")) + return "下界"; + if (structureName.contains("猪灵堡垒") || structureName.contains("堡垒遗迹")) + return "下界"; + if (structureName.contains("玄武岩结构") || structureName.contains("下界化石")) + return "下界"; + + // 末地结构 + if (structureName.contains("末地城")) + return "末地"; + if (structureName.contains("末地船")) + return "末地"; + + return ""; + } + + +// ========== 解析为区块 ========== + + private List
parseIntoSections( + List lines) { + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + return parseHashSections(lines); + } else if (hasDash) { + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + continue; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseHashSections(converted); + } + return new ArrayList<>(); + } + + private List
parseHashSections( + List lines) { + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add(new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } else if (t.isEmpty()) { + // skip + } else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty() + && curTitle != null) { + curLines.add(cond); + } + } + } + if (curTitle != null) { + sections.add(new Section( + curTitle, curLines)); + } + return sections; + } + + private List
buildFlatSections( + List lines, + String category) { + List conditions = + new ArrayList<>(); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + conditions.add(cond); + } + } + if (conditions.isEmpty()) + return new ArrayList<>(); + List
list = + new ArrayList<>(); + list.add(new Section( + "第一阶段", conditions)); + return list; + } + + // ========== 文件加载入口 ========== + + private void loadAllQuests() { + loadCategory("新人任务"); + loadCategory("主线任务"); + loadCategory("支线任务"); + } + + private void loadCategory(String name) { + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 扫描中..."); + File folder = new File( + plugin.getDataFolder(), + name); + String path = "plugins/Sdf1_login/" + + name + "/"; + + if (!folder.exists()) { + folder.mkdirs(); + plugin.getLogger().warning( + "[QuestTracker] 已创建目录: " + + path); + questCache.put(name, + new ArrayList<>()); + return; + } + + File[] mdFiles = folder.listFiles( + (d, n) -> n.endsWith(".md")); + + if (mdFiles == null + || mdFiles.length == 0) { + plugin.getLogger().warning( + "[QuestTracker] " + path + + " 下无 .md 文件"); + questCache.put(name, + new ArrayList<>()); + return; + } + Arrays.sort(mdFiles); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 发现 " + mdFiles.length + + " 个文件,开始扫描..."); + + // 合并所有文件的阶段 + TreeMap merged = + new TreeMap<>(); + int fileCount = 0; + + for (int fi = 0; + fi < mdFiles.length; fi++) { + File file = mdFiles[fi]; + List lines = + readFileLines(file); + + if (lines.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 空文件,跳过"); + continue; + } + + List
sections = + parseIntoSections(lines); + + if (sections.isEmpty()) { + sections = buildFlatSections( + lines, name); + } + + if (sections.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 无法识别,跳过"); + continue; + } + + // 每个文件独立处理阶段号 + int defaultNum = fi + 1; + buildSingleFileStages( + sections, name, + merged, defaultNum); + + fileCount++; + } + + if (merged.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + name + + ": 无有效任务数据"); + questCache.put(name, + new ArrayList<>()); + return; + } + + // 日志 + for (Map.Entry en + : merged.entrySet()) { + int num = en.getKey() >= 1000 + ? en.getKey() - 999 + : en.getKey(); + plugin.getLogger().info( + "[QuestTracker] " + name + + " 阶段" + num + + " \"" + + en.getValue().displayName + + "\" → 条件" + + en.getValue() + .conditions.size() + + "项, 详情" + + en.getValue() + .details.size() + + "项, 奖励" + + en.getValue() + .rewards.size() + + "项"); + } + + List list = + new ArrayList<>( + merged.values()); + questCache.put(name, list); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 解析完成 → " + + list.size() + " 个阶段" + + " (来自 " + fileCount + + " 个文件)"); + } + + private Integer extractStageNumber( + String title) { + if (title == null) return null; + String t = title.toLowerCase().trim(); + + // 含阶段/stage/关/chapter 关键词 + boolean isStage = + t.contains("阶段") + || t.contains("stage") + || t.contains("关卡") + || t.contains("章节") + || t.contains("chapter"); + + if (isStage) { + String digits = title + .replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + try { + return Integer.parseInt(digits); + } catch (NumberFormatException e) { + // fall through + } + } + return parseChineseNumber(title); + } + + // 无关键词但以数字开头(如 "1. 挖掘") + String trimmed = t + .replaceAll("^[.、\\s]+", "") + .trim(); + if (trimmed.length() > 0 + && Character.isDigit( + trimmed.charAt(0))) { + String num = trimmed + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) { + try { + return Integer.parseInt(num); + } catch (NumberFormatException e) { + // fall through + } + } + } + + return null; + } +// ========== 领取记录 ========== + + /** + * 标记已领取某阶段奖励 + */ + public void markClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return; + // 用阶段编号代替显示名,保证稳定 + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + storage.setCondition(player, key); + } + + public boolean hasClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return false; + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + return toBool( + storage.getCondition( + player, key)); + } + + private int parseChineseNumber( + String title) { + String[] cn = {"一", "二", "三", + "四", "五", "六", "七", + "八", "九", "十"}; + if (title.contains("十")) { + for (int j = 0; j < 9; j++) { + if (title.contains(cn[j])) { + return 10 + j + 1; + } + } + return 10; + } + for (int i = 0; i < 9; i++) { + if (title.contains(cn[i])) { + return i + 1; + } + } + return 0; + } + + // ========== md 解析核心 ========== + + /** + * 读取文件所有行 + */ + private List readFileLines( + File file) { + List lines = + new ArrayList<>(); + try { + BufferedReader br = + new BufferedReader( + new FileReader(file)); + String line; + while ((line = br.readLine()) + != null) { + lines.add(line); + } + br.close(); + } catch (Exception e) { + plugin.getLogger().warning( + "[QuestTracker] 读取 " + + file.getName() + + " 失败: " + + e.getMessage()); + } + return lines; + } + + /** + * 自动检测格式并解析单个文件 + */ + private List parseMdFile( + File file, String category) { + List lines = + readFileLines(file); + if (lines.isEmpty()) + return new ArrayList<>(); + + // 检测分隔符 + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) + hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + // # 分隔格式 + return parseSections( + lines, category); + } else if (hasDash) { + // ---- 分隔格式:转换为 # 后复用 + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + title = "separator"; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseSections( + converted, category); + } else { + // 无分隔符:整文件一个阶段 + return parseFlat(lines, category); + } + } + + /** + * # 分隔格式 → 区块 → 阶段 + */ + private List parseSections( + List lines, + String category) { + // 拆分为区块 + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + + // # 开头 → 新区块 + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add( + new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } + // 空行/分隔线 → 跳过 + else if (t.isEmpty() + || t.startsWith("----") + || t.startsWith("====") + || t.startsWith("***")) { + // skip + } + // 普通行 → 区块内容 + else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + curLines.add(cond); + } + } + } + // 保存最后一个区块 + if (curTitle != null) { + sections.add( + new Section( + curTitle, curLines)); + } + + // 直接转为 QuestFile 列表 + List result = + new ArrayList<>(); + int num = 1; + for (Section sec : sections) { + QuestFile qf = new QuestFile(); + qf.displayName = + extractDisplayName( + sec.title); + qf.fileName = category; + qf.conditions = + new ArrayList<>(sec.lines); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + result.add(qf); + num++; + } + return result; + } + + /** + * 无分隔符:整文件 = 一个阶段 + */ + private List parseFlat( + List lines, + String category) { + List list = + new ArrayList<>(); + QuestFile qf = makeDefaultStage( + category); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + qf.conditions.add(cond); + } + } + if (!qf.conditions.isEmpty()) { + list.add(qf); + } + return list; + } +// ========== Vault 经济奖励发放 ========== + + /** + * 发放阶段奖励 + * 含经济内容的走 Vault API或债券 + */ + public void claimRewards( + Player player, + QuestFile qf) { + if (qf == null + || qf.rewards == null + || qf.rewards.isEmpty()) { + player.sendMessage("§c无可领取的奖励"); + return; + } + + Main main = (Main) plugin; + // ★ 读取配置:economy = Vault,bonds = 债券(默认) + boolean useEconomy = "economy".equalsIgnoreCase( + main.getConfigMgr().rewardChannel); + + // Vault 经济引用(仅 useEconomy 时使用) + net.milkbowl.vault.economy.Economy econ = null; + if (useEconomy) { + econ = main.getEconomy(); + } + + int sentCount = 0; + + for (String reward : qf.rewards) { + String rt = reward.trim(); + if (rt.isEmpty()) continue; + + // 检查可选奖励标记 + if (hasOptionalMarker(rt)) { + if (!areAllConditionsMet(player.getName(), qf)) { + player.sendMessage( + "§7[可选奖励] 需完成所有可选条件: " + rt); + continue; + } + } + + // 按 + 分割子项 + String[] parts = rt.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 尝试识别经济金额 + double amt = extractEconomyAmount(part); + if (amt > 0) { + // ★ 根据配置分发 + if (useEconomy && econ != null) { + // —— Vault 经济 —— + double before = + econ.getBalance(player); + econ.depositPlayer(player, amt); + double after = + econ.getBalance(player); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(余额: §e" + + String.format("%.2f", before) + + " §7→ §a" + + String.format("%.2f", after) + + "§7)"); + if (plugin instanceof Main) { + ((Main) plugin).getCommission() + .payCommission( + player.getName(), amt); + } + } else { + // —— 债券(默认) —— + int bondAmt = + (int) Math.round(amt); + BondManager bonds = main.getBonds(); + if (bonds != null) { + int before = bonds.getBonds( + player.getName()); + bonds.addBonds( + player.getName(), + bondAmt, + "quest_reward", + qf.displayName, + "任务系统", + "任务奖励:" + qf.displayName); + int after = bonds.getBonds( + player.getName()); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(债券: §e" + + before + + " §7→ §a" + + after + "§7)"); + } else { + player.sendMessage( + "§c债券系统不可用"); + } + } + sentCount++; + continue; + } + + // 尝试发物品 + java.util.List items = + parseRewardItems(part); + if (!items.isEmpty()) { + for (ItemStack is : items) { + player.getInventory().addItem(is); + } + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + continue; + } + + // 非经济非物品 → 仅消息 + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + } + } + + plugin.getLogger().info( + "[QuestTracker] " + player.getName() + + " 预取了 \"" + qf.displayName + + "\" 共 " + sentCount + "项奖励"); + } + + /** + * 检查奖励文本是否含可选标记 + */ + private boolean hasOptionalMarker( + String text) { + return text.contains("(可选)") + || text.contains("(进阶)") + || text.contains("【可选】") + || text.contains("【进阶】"); + } + + /** + * 检查阶段所有条件(含可选)是否全部满足 + * 用于决定可选奖励是否发放 + */ + private boolean areAllConditionsMet( + String player, QuestFile qf) { + if (qf.conditions == null + || qf.conditions.isEmpty()) + return true; + for (String cond : qf.conditions) { + if (!isConditionMet(player, cond)) + return false; + } + return true; + } + + /** + * 从奖励文本中提取经济金额 + * 支持: "500游戏币" "500" "金币500" + * "$500" "1000-5000"(随机) "1000~5000" + */ + private double extractEconomyAmount( + String text) { + String t = text.replaceAll(" ", ""); + + // 必须包含经济关键词才算经济奖励 + boolean hasEcon = + t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额") + || t.contains("发钱") + || t.contains("债券"); // ★ 新增 + + + if (!hasEcon) return 0; + + // 范围: "1000-5000" 或 "1000~5000" + Matcher rm = Pattern + .compile("(\\d+)[-~](\\d+)") + .matcher(t); + if (rm.find()) { + double min = Double.parseDouble( + rm.group(1)); + double max = Double.parseDouble( + rm.group(2)); + return min + Math.random() + * (max - min); + } + + // 纯数字(含 $ 前缀) + Matcher nm = Pattern + .compile("\\$?(\\d+(\\.\\d+)?)") + .matcher(t); + if (nm.find()) { + return Double.parseDouble( + nm.group(1)); + } + + return 0; + } + /** + * 解析奖励物品文本 + * "皮革套装*1" → 4件皮革装备 + * "木剑*1" → 1把木剑 + * "钻石*64" → 64颗钻石 + */ + private List parseRewardItems( + String text) { + List result = + new ArrayList<>(); + + // 去掉条件前缀 + // 如 "完成注册登录:皮革套装*1" + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + + // 按 + 分割多个物品 + String[] parts = + text.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 跳过经济文本 + if (extractEconomyAmount(part) > 0) + continue; + + int amount = 1; + String itemName = part; + + // 解析 *N 数量 + if (part.contains("*")) { + String[] sp = + part.split("\\*"); + itemName = sp[0].trim(); + try { + amount = Integer.parseInt( + sp[1].trim()); + } catch (Exception e) { + amount = 1; + } + } + + List mats = + matchMaterials(itemName); + for (Material m : mats) { + int give = m.name() + .contains("HELMET") + || m.name() + .contains("CHESTPLATE") + || m.name() + .contains("LEGGINGS") + || m.name() + .contains("BOOTS") + ? 1 : amount; + result.add( + new ItemStack(m, give)); + } + } + return result; + } + + /** + * 中文物品名 → Material 列表 + * 套装返回多个部件 + */ + private List matchMaterials( + String name) { + List list = + new ArrayList<>(); + String n = name.toLowerCase().trim(); + + // 套装 + if (n.contains("皮革套装") + || n.contains("皮套装")) { + list.add(Material.LEATHER_HELMET); + list.add(Material.LEATHER_CHESTPLATE); + list.add(Material.LEATHER_LEGGINGS); + list.add(Material.LEATHER_BOOTS); + return list; + } + if (n.contains("铁套装") + || n.contains("铁甲")) { + list.add(Material.IRON_HELMET); + list.add(Material.IRON_CHESTPLATE); + list.add(Material.IRON_LEGGINGS); + list.add(Material.IRON_BOOTS); + return list; + } + if (n.contains("钻石套装") + || n.contains("钻甲")) { + list.add(Material.DIAMOND_HELMET); + list.add(Material.DIAMOND_CHESTPLATE); + list.add(Material.DIAMOND_LEGGINGS); + list.add(Material.DIAMOND_BOOTS); + return list; + } + // 木制工具套装 + if (n.contains("木制工具") + || n.contains("木工具")) { + list.add(Material.WOODEN_SWORD); + list.add(Material.WOODEN_PICKAXE); + list.add(Material.WOODEN_AXE); + list.add(Material.WOODEN_SHOVEL); + return list; + } + if (n.contains("石制工具") + || n.contains("石工具")) { + list.add(Material.STONE_SWORD); + list.add(Material.STONE_PICKAXE); + list.add(Material.STONE_AXE); + list.add(Material.STONE_SHOVEL); + return list; + } + if (n.contains("铁工具")) { + list.add(Material.IRON_SWORD); + list.add(Material.IRON_PICKAXE); + list.add(Material.IRON_AXE); + list.add(Material.IRON_SHOVEL); + return list; + } + + // 单个物品 + if (n.contains("木剑")) + list.add(Material.WOODEN_SWORD); + else if (n.contains("木镐")) + list.add(Material.WOODEN_PICKAXE); + else if (n.contains("铁剑")) + list.add(Material.IRON_SWORD); + else if (n.contains("铁镐")) + list.add(Material.IRON_PICKAXE); + else if (n.contains("钻石剑")) + list.add(Material.DIAMOND_SWORD); + else if (n.contains("钻石镐")) + list.add(Material.DIAMOND_PICKAXE); + else if (n.contains("钻石")) + list.add(Material.DIAMOND); + else if (n.contains("铁锭")) + list.add(Material.IRON_INGOT); + else if (n.contains("金锭") + || n.contains("金块")) + list.add(Material.GOLD_INGOT); + else if (n.contains("煤炭")) + list.add(Material.COAL); + else if (n.contains("木棍")) + list.add(Material.STICK); + else if (n.contains("弓")) + list.add(Material.BOW); + else if (n.contains("箭")) + list.add(Material.ARROW); + else if (n.contains("床")) + list.add(Material.RED_BED); + else { + // 尝试直接匹配 Material 名 + try { + list.add(Material.valueOf( + n.toUpperCase() + .replaceAll("[^A-Z_]", + ""))); + } catch (Exception e) { + // 未知物品 + } + } + return list; + } + + private QuestFile getOrCreateStage( + TreeMap map, + int key, + String category) { + if (!map.containsKey(key)) { + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + key; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + } + return map.get(key); + } + + /** + * 单个文件的区块 → 阶段 + * defaultStageNum: 文件无阶段标题时的默认编号 + */ + private void buildSingleFileStages( + List
sections, + String category, + TreeMap stageMap, + int defaultStageNum) { + Integer curKey = null; + + for (Section sec : sections) { + Integer stageNum = + extractStageNumber(sec.title); + + if (stageNum != null) { + curKey = stageNum; + } else if (curKey == null) { + curKey = defaultStageNum; + } + + QuestFile qf = getOrCreateStage( + stageMap, curKey, category); + + String displayName = + extractDisplayName(sec.title); + + // 设置显示名 + if (!displayName.isEmpty() + && qf.displayName + .startsWith("阶段 ")) { + qf.displayName = displayName; + } + + // 路由 + if (stageNum != null + && sec.lines.isEmpty()) { + if (!displayName.isEmpty()) { + qf.displayName = displayName; + } + } + else if (isRewardTitle(sec.title)) { + // 奖励 → 只进 rewards + qf.rewards.addAll(sec.lines); + } + else if (isDetailsTitle(sec.title)) { + // 详情 → 进 details + conditions + qf.details.addAll(sec.lines); + for (String line : sec.lines) { + if (!qf.conditions + .contains(line)) { + qf.conditions.add(line); + } + } + } + else if (isConditionsTitle( + sec.title)) { + // 条件 → 只进 conditions + qf.conditions.addAll(sec.lines); + } + else { + // 其他 → 条件 + qf.conditions.addAll(sec.lines); + } + } + } + + /** + * "主线任务-斗志昂扬" → "斗志昂扬" + * "支线-钓鱼" → "钓鱼" + * "阶段 1" → "阶段 1" + * "第一阶段" → "第一阶段" + */ + private String extractDisplayName( + String title) { + if (title == null) return ""; + // 按 - 或 — 或 – 分割 + String[] parts = title.split( + "\\s*[-—–]\\s*", 2); + if (parts.length >= 2) { + String right = parts[1].trim(); + if (!right.isEmpty()) { + return right; + } + } + return title; + } + private DatabaseManager getDb() { + return ((Main) plugin).getDb(); + } + + private boolean isDetailsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("任务详情") + || t.contains("详情") + || t.contains("任务说明") + || t.contains("任务目标") + || t.contains("description") + || t.contains("任务要求"); + } + + private boolean isConditionsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("解锁条件") + || t.contains("条件") + || t.contains("前置条件") + || t.contains("前置要求") + || t.contains("解锁") + || t.contains("condition"); + } + + + private void ensureStage( + TreeMap map, + String category, + QuestFile current, + int counter) { + if (current != null) return; + // 用大数字确保排在有编号阶段后面 + int key = 1000 + counter; + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + (counter + 1); + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + // 更新调用方的引用(通过返回值) + // 这里用 map 操作,current 无法直接更新 + // 所以改用返回值方式 + } + /** + * 自动将条件中的奖励内容移到 rewards + */ + private void separateRewards(QuestFile qf) { + List remaining = + new ArrayList<>(); + for (String line : qf.conditions) { + if (isRewardContent(line)) { + if (!qf.rewards.contains(line)) { + qf.rewards.add(line); + } + } else { + remaining.add(line); + } + } + qf.conditions = remaining; + } + private boolean checkStageDependency( + String player, + String condition) { + Matcher m = Pattern.compile( + "第([一二三四五六七八九十\\d]+)" + + "阶段") + .matcher(condition); + + while (m.find()) { + String numStr = m.group(1); + int stageNum = + parseChineseNumber(numStr); + if (stageNum == 0) { + try { + stageNum = + Integer.parseInt(numStr); + } catch (Exception e) { + return false; + } + } + + boolean stageDone = false; + for (List quests + : questCache.values()) { + for (QuestFile qf : quests) { + Integer qfNum = + extractStageNumber( + qf.displayName); + if (qfNum != null + && qfNum == stageNum) { + if (isStageComplete(player, + qf.conditions)) { + stageDone = true; + } + break; + } + } + if (stageDone) break; + } + + if (!stageDone) return false; + } + return true; + } + + /** + * 判断一行内容是否为奖励 + */ + private boolean isRewardContent( + String line) { + String l = line.toLowerCase(); + // 含货币关键词 + if (l.contains("游戏币") + || l.contains("金币") + || l.contains("积分") + || l.contains("钻石") + || l.contains("发钱") + || l.contains("发放") + || l.contains("经验") + || l.contains("等级") + || l.contains("经济") + || l.contains("余额") + || l.contains("块钱")) + return true; + // 纯数字+单位(如 "500游戏币") + if (l.matches( + ".*\\d+\\s*(游戏币|金币|元|积分|" + + "钻石|块钱|经验|级|经济|余额).*")) + return true; + // $前缀(如 "$500") + if (l.matches(".*\\$\\d+.*")) + return true; + // 范围格式(如 "1000-5000" 或 "1000~5000") + if (l.matches(".*\\d+[-~]\\d+.*") + && !l.contains("阶段") + && !l.contains("stage")) + return true; + return false; + } + + // ========== 标题分类 ========== + + private boolean isStageTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("阶段") + || t.contains("stage") + || t.contains("第一") + || t.contains("第二") + || t.contains("第三") + || t.contains("第四") + || t.contains("第五"); + } + + private boolean isRewardTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("奖励") + || t.contains("奖品") + || t.contains("报酬") + || t.contains("reward") + || t.contains("奖赏") + || t.contains("礼包内容") + || t.contains("礼包") + || t.contains("物品奖励"); + } + + + private QuestFile makeDefaultStage( + String category) { + QuestFile qf = new QuestFile(); + qf.displayName = "第一阶段"; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + return qf; + } + + private static class Section { + final String title; + final List lines; + Section(String title, + List lines) { + this.title = title; + this.lines = lines; + } + } + + // ========== 公开查询 ========== + + public List getQuests( + String category) { + List list = + questCache.get(category); + return list != null ? list + : new ArrayList<>(); + } + + public int getCompletedStageCount( + String player, + List quests) { + int count = 0; + if (quests == null) return 0; + for (QuestFile qf : quests) { + if (isStageComplete(player, + qf.conditions)) { + count++; + } + } + return count; + } + + public QuestFile getCurrentStage( + String player, + List quests) { + if (quests == null) return null; + for (QuestFile qf : quests) { + if (!isStageComplete( + player, qf.conditions)) { + return qf; + } + } + return null; + } + + public int getStageNumber( + List quests, + QuestFile qf) { + if (quests == null || qf == null) + return 0; + return quests.indexOf(qf) + 1; + } + + public int getStageNumber( + QuestFile qf) { + if (qf == null) return 0; + List quests = + getQuests(qf.fileName); + return getStageNumber(quests, qf); + } + + public boolean isStageCompleted( + String player, + QuestFile qf) { + if (qf == null) return false; + return isStageComplete( + player, qf.conditions); + } + + public List getUnmetConditions( + String player, + List conditions) { + List unmet = + new ArrayList<>(); + if (conditions == null) return unmet; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) { + unmet.add( + getProgress( + player, cond)); + } + } + return unmet; + } + public String getProgress( + String player, String rawCond) { + if (player == null || rawCond == null) + return "§7未知"; + String key = stripOptional(rawCond); + + if (key.contains("打卡") + && key.contains("群系")) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return "§7打卡 " + biome + + "群系: §e" + + catCount + "/1"; + } + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + + if (key.contains("完成") + && key.contains("阶段")) { + boolean done = checkStageDependency( + player, key); + return done + ? "§a✓ " + key + : "§c✗ " + key; + } + + if ("完成注册登录".equals(key)) { + boolean done = getDb() != null + && getDb().userExists( + player); + return done + ? "§a✓ 完成注册登录" + : "§c✗ 完成注册登录"; + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + boolean done = val != null + && !val.toString().isEmpty(); + return done + ? "§a✓ 绑定Email" + : "§c✗ 绑定Email"; + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + needed + "小时"; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + int cur = getCounter( + player, "CHAT"); + return "§7聊天: §e" + + cur + "/1"; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return "§7方块操作: §e" + + total + "/" + needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return "§7连续登录: §e" + + streak + "/" + needed + "天"; + } + + if (key.startsWith("MINE:")) { + String[] parts = key.split(":"); + if (parts.length >= 3) { + int needed = + parseInt(parts[2]); + int cur = getCounter( + player, + "MINE:" + parts[1]); + return "§7挖 " + parts[1] + + ": §e" + cur + + "/" + needed; + } + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "VILLAGER_TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + + } + + // ===== 击杀泛解析(支持中文列表) ===== +// 匹配 "击杀10只骷髅、僵尸、苦力怕" 等 + if (key.contains("击杀") && key.contains("只")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + + String mobPart = key; + java.util.regex.Matcher killM = + java.util.regex.Pattern + .compile("击杀\\d+只(.+)") + .matcher(key); + if (killM.find()) { + mobPart = killM.group(1).trim(); + } + + String[] mobNames = mobPart + .split("[、,,,]+"); + int totalKills = 0; + StringBuilder names = new StringBuilder(); + for (String name : mobNames) { + String trimmed = name.trim(); + if (trimmed.isEmpty()) continue; + String entityType = + chineseMobToEntity(trimmed); + if (!entityType.isEmpty()) { + totalKills += getCounter( + player, + "KILL:" + entityType); + if (names.length() > 0) + names.append("/"); + names.append(trimmed); + } + } + return "§7击杀" + names + ": §e" + + totalKills + "/" + needed; + } + +// 单一KILL:xxx:N 格式(原有逻辑保留) + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + int needed = parseInt(p[2]); + int cur = getCounter(player, + "KILL:" + p[1]); + return "§7击杀" + p[1] + ": §e" + + cur + "/" + needed; + } + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "KILL_PLAYER"); + return "§7击杀玩家: §e" + + cur + "/" + needed; + } + + if (key.startsWith("CRAFT")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "CRAFT"); + return "§7合成: §e" + + cur + "/" + needed; + } + + if (key.startsWith("FISH")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "FISH"); + return "§7钓鱼: §e" + + cur + "/" + needed; + } + + if (key.startsWith("PLAYTIME")) { + String[] parts = key.split(":"); + long needed = parts.length >= 2 + ? Long.parseLong(parts[1]) + : 0; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + formatMs(needed); + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7上报村庄: §e" + + cur + "/" + needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7探索村庄: §e" + + cur + "/1"; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return "§7近7天登录: §e" + + streak + "/" + + needed + "天"; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7新手任务: §e" + + done + "/" + + quests.size(); + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7主线任务: §e" + + done + "/" + + quests.size(); + } + // ===== 泛解析:击杀类进度 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < mobTypes.size(); i++) { + total += getCounter(player, + "KILL:" + mobTypes.get(i)); + if (i > 0) names.append("/"); + names.append(reverseMobName( + mobTypes.get(i))); + } + return "§7击杀" + names + ": §e" + + total + "/" + needed; + } + } + + // ===== 泛解析:村民交易进度 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + // ===== 泛解析:采集/挖矿进度 ===== + if (key.contains("采集") + || key.contains("挖")) { + int needed = + parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < matTypes.size(); i++) { + total += getCounter( + player, + "MINE:" + matTypes + .get(i)); + if (i > 0) + names.append("/"); + names.append( + translateMaterialName( + matTypes + .get(i))); + } + return "§7采集" + names + + ": §e" + total + + "/" + needed; + } + } + + // ===== 泛解析:合成进度 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "CRAFT"); + return "§7合成: §e" + cur + "/" + needed; + } + + // ===== 泛解析:钓鱼进度 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "FISH"); + return "§7钓鱼: §e" + cur + "/" + needed; + } + + return "§7" + key; + } + private String translateMaterialName( + String name) { + switch (name) { + case "COAL_ORE": + return "煤矿石"; + case "DEEPSLATE_COAL_ORE": + return "深层煤矿石"; + case "IRON_ORE": + return "铁矿石"; + case "DEEPSLATE_IRON_ORE": + return "深层铁矿石"; + case "COPPER_ORE": + return "铜矿石"; + case "DEEPSLATE_COPPER_ORE": + return "深层铜矿石"; + case "GOLD_ORE": + return "金矿石"; + case "DEEPSLATE_GOLD_ORE": + return "深层金矿石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "DEEPSLATE_DIAMOND_ORE": + return "深层钻石矿石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "DEEPSLATE_EMERALD_ORE": + return "深层绿宝石矿石"; + case "LAPIS_ORE": + return "青金石矿石"; + case "DEEPSLATE_LAPIS_ORE": + return "深层青金石矿石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "DEEPSLATE_REDSTONE_ORE": + return "深层红石矿石"; + case "NETHER_GOLD_ORE": + return "下界金矿石"; + case "NETHER_QUARTZ_ORE": + return "下界石英矿石"; + case "ANCIENT_DEBRIS": + return "远古残骸"; + case "COAL": + return "煤炭"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "RAW_COPPER": + return "粗铜"; + case "DIAMOND": + return "钻石"; + case "EMERALD": + return "绿宝石"; + case "LAPIS_LAZULI": + return "青金石"; + case "REDSTONE": + return "红石"; + case "QUARTZ": + return "石英"; + default: + return name; + } + } + + /** + * 中文物种名 → Bukkit EntityType 名 + * 支持击杀泛解析的模糊匹配 + */ + private String chineseMobToEntity(String cn) { + String n = cn.toLowerCase().trim(); + + // 直接别名表 + Map mobMap = new HashMap<>(); + mobMap.put("骷髅", "SKELETON"); + mobMap.put("僵尸", "ZOMBIE"); + mobMap.put("苦力怕", "CREEPER"); + mobMap.put("蜘蛛", "SPIDER"); + mobMap.put("洞穴蜘蛛", "CAVE_SPIDER"); + mobMap.put("小白", "SKELETON"); + mobMap.put("末影人", "ENDERMAN"); + mobMap.put("enderman", "ENDERMAN"); + mobMap.put("猪灵", "PIGLIN"); + mobMap.put("猪灵蛮兵", "PIGLIN_BRUTE"); + mobMap.put("恶魂", "GHAST"); + mobMap.put("凋灵骷髅", "WITHER_SKELETON"); + mobMap.put("烈焰人", "BLAZE"); + mobMap.put("史莱姆", "SLIME"); + mobMap.put("岩浆怪", "MAGMA_CUBE"); + mobMap.put("女巫", "WITCH"); + mobMap.put("蠹虫", "SILVERFISH"); + mobMap.put("潜影贝", "SHULKER"); + mobMap.put("守卫者", "GUARDIAN"); + mobMap.put("远古守卫者", "ELDER_GUARDIAN"); + mobMap.put("溺尸", "DROWNED"); + mobMap.put("僵尸村民", "ZOMBIE_VILLAGER"); + mobMap.put("尸壳", "HUSK"); + mobMap.put("流浪者", "STRAY"); + mobMap.put("幻翼", "PHANTOM"); + mobMap.put("掠夺者", "PILLAGER"); + mobMap.put("卫道士", "VINDICATOR"); + mobMap.put("唤魔者", "EVOKER"); + mobMap.put("恼鬼", "VEX"); + mobMap.put("劫掠兽", "RAVAGER"); + mobMap.put("蜘蛛骑士", "SPIDER_JOCKEY"); + mobMap.put("凋灵", "WITHER"); + mobMap.put("末影龙", "ENDER_DRAGON"); + + // 精确匹配 + if (mobMap.containsKey(n)) { + return mobMap.get(n); + } + + // 模糊匹配(包含关系) + for (Map.Entry en + : mobMap.entrySet()) { + if (n.contains(en.getKey()) + || en.getKey().contains(n)) { + return en.getValue(); + } + } + + // 尝试直接用英文名 + try { + return org.bukkit.entity.EntityType + .valueOf(n.toUpperCase() + .replace(" ", "_")) + .name(); + } catch (Exception e) { + return ""; + } + } + + public List checkInBiome( + String player, + String rawBiomeKey, + String worldName, + double x, double y, double z) { + List matched = + new ArrayList<>(); + + // 32格去重 + String posKey = + player + "_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + String lastPos = + lastCheckInPos.get(posKey); + if (curPos.equals(lastPos)) { + plugin.getLogger().info( + "[QuestTracker] 打卡忽略: " + + player + + " 32格内重复"); + return matched; + } + lastCheckInPos.put(posKey, curPos); + + String category = + mapBiomeCategory( + rawBiomeKey, worldName); + // 结构检测(覆盖纯生物群系分类) + // 地下检测:Y低于表面则归类为地下 +// 覆盖废弃矿井/试炼密室/要塞/远古城市等 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + + // ===== 结构检测(覆盖纯生物群系分类)===== + // 使用 PaperMC API 检测玩家所在位置的结构 + String structureCat = + detectStructureAt( + worldName, x, y, z); + if (structureCat != null + && !structureCat.isEmpty()) { + // 结构检测成功,结构分类优先 + // (站在结构上就不算"地下") + category = structureCat; + plugin.getLogger().info( + "[QuestTracker] 结构覆盖: " + + category + + " (结构: " + + structureCat + ")"); + } else { + plugin.getLogger().info( + "[QuestTracker] 未检测到结构: world=" + + worldName + ", pos=" + + String.format("%.0f,%.0f,%.0f", x, y, z)); + } + + + // 记录总打卡 + int allCur = getCounter( + player, "biome_ALL"); + setCounter(player, "biome_ALL", + allCur + 1); + + // 记录分类打卡 + if (!category.isEmpty() + && !"主世界" + .equals(category)) { + String catKey = + "biomecat_" + category; + int catCur = getCounter( + player, catKey); + setCounter(player, catKey, + catCur + 1); + } + + // 记录具体群系 + String bioKey = + "biome_" + rawBiomeKey; + int bioCur = getCounter( + player, bioKey); + setCounter(player, bioKey, + bioCur + 1); + + // 检查所有任务条件是否匹配 + List categories = + Arrays.asList( + "新人任务", + "主线任务", + "支线任务"); + + for (String cat : categories) { + List quests = + getQuests(cat); + for (QuestFile qf : quests) { + if (isStageCompleted( + player, qf)) + continue; + for (String cond + : qf.conditions) { + if (isOptional(cond)) + continue; + String condKey = + stripOptional(cond); + if (!condKey + .contains("打卡")) + continue; + // 支持群系和结构两种打卡条件 + boolean isBiomeCond = + condKey.contains("群系") + || condKey + .contains("风景点"); + boolean isStructureCond = + condKey.contains("结构") + || condKey + .contains("村庄") + || condKey + .contains("神殿") + || condKey + .contains("神庙") + || condKey + .contains("小屋") + || condKey + .contains("雪屋") + || condKey + .contains("府邸") + || condKey + .contains("前哨站") + || condKey + .contains("矿井") + || condKey + .contains("要塞") + || condKey + .contains("城市") + || condKey + .contains("废墟") + || condKey + .contains("密室") + || condKey + .contains("沉船") + || condKey + .contains("宝箱") + || condKey + .contains("传送门") + || condKey + .contains("化石") + || condKey + .contains("堡垒"); + if (!isBiomeCond + && !isStructureCond) + continue; + String target = + extractBiome(condKey); + plugin.getLogger().info( + "[QuestTracker] 条件匹配: condKey=\"" + + condKey + "\", target=\"" + + target + "\", category=\"" + + category + "\", isBiomeCond=" + + isBiomeCond + ", isStructureCond=" + + isStructureCond); + boolean hit = false; + if ("ALL".equals(target) + || "风景点" + .equals(target)) { + hit = true; + } else if (!target.isEmpty() + && target.equals( + category)) { + hit = true; + } + if (hit && !matched + .contains(cond)) { + matched.add(cond); + } + } + } + } + + return matched; + } + + /* + * 打卡入口:报告坐标 → 32格去重 → 匹配条件 → 记录 + */ + public String checkInAtPosition( + String player, String worldName, + double x, double y, double z) { + + List history = + loadCheckInPositions(player); + for (double[] pos : history) { + double dist = Math.sqrt( + Math.pow(x - pos[0], 2) + + Math.pow(y - pos[1], 2) + + Math.pow(z - pos[2], 2)); + if (dist <= 32) { + return "§c距离上次打卡点太近(" + + String.format( + "%.0f", dist) + + "格)"; + } + } + + // ===== 获取群系和分类 ===== + String biome = "minecraft:plains"; + String category = "平原"; + String structureCat = null; + try { + org.bukkit.World w = + Bukkit.getWorld(worldName); + if (w != null) { + org.bukkit.Location loc = + new org.bukkit.Location( + w, + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + biome = loc.getBlock() + .getBiome().getKey() + .toString(); + category = mapBiomeCategory( + biome, worldName); + + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + + // ===== 结构检测(覆盖纯生物群系分类)===== + structureCat = + detectStructureAt( + worldName, x, y, z); + plugin.getLogger().info( + "[QuestTracker] checkInAtPosition 结构检测: " + + structureCat + " at " + + String.format("%.0f,%.0f,%.0f", x, y, z)); + if (structureCat != null + && !structureCat.isEmpty()) { + // 结构分类优先于"地下" + category = structureCat; + } + + if ("下界".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.NETHER) { + category = "下界"; + } else if ("末地".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.THE_END) { + category = "末地"; + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 群系读取失败: " + + ex.getMessage()); + } + + plugin.getLogger().info( + "[QuestTracker] 打卡: " + + player + + " world=" + worldName + + " biome=" + biome + + " cat=" + category + + " pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + + saveCheckInPosition( + player, x, y, z); + recordCheckIn( + player, biome, category, + worldName, x, y, z); + List matched = + checkInBiome( + player, biome, + worldName, x, y, z); + + StringBuilder sb = new StringBuilder(); + sb.append("§a打卡成功!群系: §e") + .append(category); + if (structureCat != null && !structureCat.isEmpty()) { + sb.append("\n§a结构: §e").append(structureCat); + } + if (!matched.isEmpty()) { + sb.append("\n§a完成任务条件:"); + for (String m : matched) { + String qn = + findQuestName(m); + sb.append("\n§7• "); + if (qn != null) { + sb.append("§e[") + .append(qn) + .append("§7] §f"); + } + sb.append(m); + } + } + return sb.toString(); + } + + + /** + * 根据条件文本查找所属任务名 + */ + private String findQuestName( + String condition) { + for (String cat : Arrays.asList( + "新人任务", + "主线任务", + "支线任务")) { + for (QuestFile qf : + getQuests(cat)) { + if (qf.conditions.contains( + condition)) { + return qf.displayName; + } + } + } + return null; + } + + + /** + * 加载玩家所有历史打卡坐标 + * 格式: x1,y1,z1;x2,y2,z2;... + */ + private List loadCheckInPositions( + String player) { + List list = new ArrayList<>(); + Object val = storage.getCondition( + player, "checkin_all_pos"); + if (val == null) return list; + String data = val.toString(); + if (data.isEmpty()) return list; + String[] records = data.split(";"); + for (String r : records) { + String[] parts = r.split(","); + if (parts.length >= 3) { + try { + list.add(new double[]{ + Double.parseDouble(parts[0]), + Double.parseDouble(parts[1]), + Double.parseDouble(parts[2])}); + } catch (NumberFormatException e) { + // 跳过损坏数据 + } + } + } + return list; + } + + /** + * 保存打卡坐标(追加到已有记录) + */ + private void saveCheckInPosition( + String player, double x, + double y, double z) { + List list = + loadCheckInPositions(player); + list.add(new double[]{x, y, z}); + StringBuilder sb = new StringBuilder(); + for (double[] pos : list) { + if (sb.length() > 0) sb.append(";"); + sb.append(pos[0]).append(",") + .append(pos[1]).append(",") + .append(pos[2]); + } + storage.setConditionRaw( + player, + "checkin_all_pos", + sb.toString()); + } + // ========== 中文生物名 → EntityType 映射 ========== + private static final Map + MOB_NAME_MAP = new HashMap<>(); + static { + MOB_NAME_MAP.put("洞穴蜘蛛", "CAVE_SPIDER"); + MOB_NAME_MAP.put("骷髅", "SKELETON"); + MOB_NAME_MAP.put("僵尸", "ZOMBIE"); + MOB_NAME_MAP.put("苦力怕", "CREEPER"); + MOB_NAME_MAP.put("蜘蛛", "SPIDER"); + MOB_NAME_MAP.put("末影人", "ENDERMAN"); + MOB_NAME_MAP.put("女巫", "WITCH"); + MOB_NAME_MAP.put("烈焰人", "BLAZE"); + MOB_NAME_MAP.put("僵尸猪灵", + "ZOMBIFIED_PIGLIN"); + MOB_NAME_MAP.put("猪灵", "PIGLIN"); + MOB_NAME_MAP.put("恶魂", "GHAST"); + MOB_NAME_MAP.put("凋灵骷髅", + "WITHER_SKELETON"); + MOB_NAME_MAP.put("史莱姆", "SLIME"); + MOB_NAME_MAP.put("岩浆怪", "MAGMA_CUBE"); + MOB_NAME_MAP.put("溺尸", "DROWNED"); + MOB_NAME_MAP.put("尸壳", "HUSK"); + MOB_NAME_MAP.put("流髑", "STRAY"); + MOB_NAME_MAP.put("幻翼", "PHANTOM"); + MOB_NAME_MAP.put("守卫者", "GUARDIAN"); + MOB_NAME_MAP.put("远古守卫者", + "ELDER_GUARDIAN"); + MOB_NAME_MAP.put("掠夺者", "PILLAGER"); + MOB_NAME_MAP.put("卫道士", "VINDICATOR"); + MOB_NAME_MAP.put("唤魔者", "EVOKER"); + MOB_NAME_MAP.put("恼鬼", "VEX"); + MOB_NAME_MAP.put("劫掠兽", "RAVAGER"); + MOB_NAME_MAP.put("潜影贝", "SHULKER"); + MOB_NAME_MAP.put("末影螨", "ENDERMITE"); + MOB_NAME_MAP.put("凋灵", "WITHER"); + MOB_NAME_MAP.put("末影龙", "ENDER_DRAGON"); + MOB_NAME_MAP.put("监守者", "WARDEN"); + MOB_NAME_MAP.put("嗅探兽", "SNIFFER"); + MOB_NAME_MAP.put("僵尸村民", + "ZOMBIE_VILLAGER"); + } + + /** + * 从条件文本中提取所有匹配的生物类型 + * 按名称长度降序匹配,避免短名误匹配 + */ + private List extractMobTypes( + String text) { + List types = new ArrayList<>(); + List> sorted = + new ArrayList<>( + MOB_NAME_MAP.entrySet()); + sorted.sort((a, b) -> + b.getKey().length() + - a.getKey().length()); + String remaining = text; + for (Map.Entry entry + : sorted) { + if (remaining.contains( + entry.getKey())) { + if (!types.contains( + entry.getValue())) { + types.add(entry.getValue()); + } + remaining = remaining.replace( + entry.getKey(), ""); + } + } + return types; + } + + /** + * EntityType → 中文名 + */ + private String reverseMobName( + String entityType) { + for (Map.Entry entry + : MOB_NAME_MAP.entrySet()) { + if (entry.getValue() + .equals(entityType)) { + return entry.getKey(); + } + } + return entityType; + } + + /** + * 从条件文本中提取矿物名 + * "采集任意矿物(煤炭除外)1组" + * → [COAL_ORE, IRON_ORE, GOLD_ORE, ...] + */ + private List extractMaterialTypes( + String text) { + List types = new ArrayList<>(); + // 排除列表 + List excludes = + new ArrayList<>(); + if (text.contains("煤炭除外")) { + excludes.add("COAL"); + excludes.add("COAL_ORE"); + excludes.add("DEEPSLATE_COAL_ORE"); + } + // 通配符:"矿物" "矿石" + if (text.contains("矿物") + || text.contains("矿石")) { + String[] ores = { + "COAL_ORE", + "DEEPSLATE_COAL_ORE", + "IRON_ORE", + "DEEPSLATE_IRON_ORE", + "COPPER_ORE", + "DEEPSLATE_COPPER_ORE", + "GOLD_ORE", + "DEEPSLATE_GOLD_ORE", + "DIAMOND_ORE", + "DEEPSLATE_DIAMOND_ORE", + "EMERALD_ORE", + "DEEPSLATE_EMERALD_ORE", + "LAPIS_ORE", + "DEEPSLATE_LAPIS_ORE", + "REDSTONE_ORE", + "DEEPSLATE_REDSTONE_ORE", + "NETHER_GOLD_ORE", + "NETHER_QUARTZ_ORE", + "ANCIENT_DEBRIS" + }; + for (String ore : ores) { + if (!excludes.contains(ore)) { + types.add(ore); + } + } + return types; + } + // 单个矿物名 + if (text.contains("煤炭")) + types.add("COAL"); + if (text.contains("铁矿") + || text.contains("铁锭")) + types.add("RAW_IRON"); + if (text.contains("金矿") + || text.contains("金锭")) + types.add("RAW_GOLD"); + if (text.contains("钻石")) + types.add("DIAMOND"); + if (text.contains("绿宝石")) + types.add("EMERALD"); + if (text.contains("青金石")) + types.add("LAPIS_LAZULI"); + if (text.contains("红石")) + types.add("REDSTONE"); + if (text.contains("铜")) + types.add("RAW_COPPER"); + if (text.contains("下界石英") + || text.contains("石英")) + types.add("QUARTZ"); + return types; + } + + + /** + * 从条件文本中提取目标群系分类 + * "打卡上报地狱任意群系" → "下界" + * "打卡任意海洋群系" → "海洋" + * "打卡任意群系" → "ALL" + * "打卡任意风景点" → "" + * "打卡村庄" → "村庄" + * "打卡沙漠神殿" → "沙漠" + */ + private String extractBiome(String text) { + if (text == null) return ""; + + // 优先匹配结构名称(更具体) + String structureResult = + mapStructureCategory(text); + if (!structureResult.isEmpty()) { + return structureResult; + } + + // 逐个匹配分类关键词 + for (Map.Entry en + : CATEGORY_KEYWORDS.entrySet()) { + if (text.contains(en.getKey())) { + return en.getValue(); + } + } + if (text.contains("风景点") + || text.contains("风景")) { + return "ALL"; + } + + // 有"群系"但无具体分类 → ALL + if (text.contains("群系")) { + return "ALL"; + } + return ""; + } + + + // ========== 标记条件 ========== + + public void markConditionComplete( + String player, + String conditionName) { + if (player == null + || conditionName == null) + return; + storage.setCondition( + player, conditionName); + } + + // ========== 事件回调 ========== + + public void onBlockMined( + String player, String material) { + String key = "MINE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + // 总计数 + setCounter(player, "MINE:ALL", + getCounter(player, "MINE:ALL") + + 1); + } + public boolean onReportVillage( + String player, + String worldName, + double x, double y, double z) { + String dbKey = "village_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + Object lastObj = + storage.getCondition( + player, dbKey); + String lastPos = lastObj != null + ? lastObj.toString() : ""; + if (curPos.equals(lastPos)) { + return false; + } + storage.setConditionRaw( + player, dbKey, curPos); + + String key = "REPORT_VILLAGE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + return true; + } + + public void onMobKill( + String player, String mobType) { + String key = "KILL:" + mobType; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayerKill( + String player) { + String key = "KILL_PLAYER"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onItemCrafted( + String player) { + String key = "CRAFT"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onFishCaught( + String player) { + String key = "FISH"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayTime( + String player, long ms) { + String key = "PLAYTIME"; + long cur = getCounterLong( + player, key); + setCounter(player, key, cur + ms); + } + + // ========== 内部:条件判定 ========== + + private boolean isStageComplete( + String player, + List conditions) { + if (conditions == null + || conditions.isEmpty()) + return true; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) + return false; + } + return true; + } + + public boolean isConditionMet( + String player, + String rawCond) { + if (player == null + || rawCond == null) + return false; + + String key = stripOptional(rawCond); + + // 无条件类文本直接通过 + if (key.equals("无前级条件") + || key.equals("无条件") + || key.equals("无") + || key.equals("none") + || key.equals("无需条件") + || key.equals("无解锁条件")) { + return true; + } + + if (key.contains("打卡") + && (key.contains("群系") + || key.contains("风景点") + || key.contains("结构") + || key.contains("村庄") + || key.contains("神殿") + || key.contains("神庙") + || key.contains("小屋") + || key.contains("雪屋") + || key.contains("府邸") + || key.contains("前哨站") + || key.contains("矿井") + || key.contains("要塞") + || key.contains("城市") + || key.contains("废墟") + || key.contains("密室") + || key.contains("废墟") + || key.contains("沉船") + || key.contains("宝箱") + || key.contains("传送门") + || key.contains("化石") + || key.contains("堡垒"))) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return total >= 1; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return catCount >= 1; + } + return getCounter( + player, "biome_ALL") >= 1; + } + + if (key.contains("完成") + && key.contains("阶段")) { + return checkStageDependency( + player, key); + } + + if ("完成注册登录".equals(key)) { + return getDb() != null + && getDb().userExists( + player); + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + return val != null + && !val.toString().isEmpty(); + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return cur >= needed * 3600000L; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + return getCounter( + player, "CHAT") >= 1; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return total >= needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return streak >= needed; + } + + if (key.startsWith("MINE:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "MINE:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "KILL:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "VILLAGER_TRADE") + >= needed; + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter(player, + "KILL_PLAYER") >= n; + } + + if (key.startsWith("CRAFT")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "CRAFT") >= n; + } + + if (key.startsWith("FISH")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "FISH") >= n; + } + + if (key.startsWith("PLAYTIME")) { + String[] p = key.split(":"); + long n = p.length >= 2 + ? Long.parseLong(p[1]) : 0; + return getCounterLong(player, + "PLAYTIME") >= n; + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "REPORT_VILLAGE") + >= needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + return getCounter( + player, "REPORT_VILLAGE") + >= 1; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return streak >= needed; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + // ===== 泛解析:击杀类中文条件 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + for (String mobType : mobTypes) { + total += getCounter(player, + "KILL:" + mobType); + } + return total >= needed; + } + } + + // ===== 泛解析:村民交易条件 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "TRADE") + >= needed; + } + + // ===== 泛解析:采集/挖矿类条件 ===== + if (key.contains("采集") || key.contains("挖")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + for (String mat : matTypes) { + total += getCounter(player, + "MINE:" + mat); + } + return total >= needed; + } + } + + // ===== 泛解析:合成类条件 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "CRAFT") + >= needed; + } + + // ===== 泛解析:钓鱼类条件 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "FISH") + >= needed; + } + + return false; + } + + public void debugLogin(String player) { + try { + var user = getDb().getUser(player); + if (user != null) { + plugin.getLogger().info( + "[QuestTracker] " + + "=== " + player + + " 数据库全部字段 ==="); + for (var entry + : user.entrySet()) { + plugin.getLogger().info( + "[QuestTracker] " + + entry.getKey() + + " = " + + entry.getValue() + + " (" + + (entry.getValue() + != null + ? entry.getValue() + .getClass() + .getSimpleName() + : "null") + + ")"); + } + } else { + plugin.getLogger().info( + "[QuestTracker] " + + "getUser返回null: " + + player); + } + } catch (Exception e) { + plugin.getLogger().info( + "[QuestTracker] " + + "debug异常: " + + e.getMessage()); + } + } + + // ========== 内部:计数器 ========== + + private int getCounter( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + return 0; + } + private int getIntField( + String player, String field) { + try { + Object val = getDb().getField( + player, field); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val != null) { + return Integer.parseInt( + val.toString().trim()); + } + } catch (Exception e) { + // 忽略 + } + return 0; + } + + public void onVillagerTrade(String player) { + String key = "VILLAGER_TRADE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + private long getCounterLong( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).longValue(); + } + return 0; + } + + private void setCounter( + String player, String key, + int value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + private void setCounter( + String player, String key, + long value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + // ========== 工具 ========== + + private boolean toBool(Object val) { + if (val == null) return false; + if (val instanceof Boolean) + return (Boolean) val; + if (val instanceof Number) + return ((Number) val) + .intValue() != 0; + String s = String.valueOf(val); + return "1".equals(s) + || "true" + .equalsIgnoreCase(s); + } + + public boolean isOptional(String cond) { + if (cond == null) return false; + String c = cond; + return c.contains("(进阶)") + || c.contains("(可选)") + || c.contains(",可选)") + || c.contains(",可选)") + || c.contains("【可选】") + || c.contains("【进阶】"); + } + + + private String stripOptional(String cond) { + if (cond == null) return ""; + return cond.replace("(进阶)", "") + .replace("(可选)", "") + .replace(",可选)", "") + .replace(",可选)", "") + .replace("【可选】", "") + .replace("【进阶】", "") + .trim(); + } + + private int parseInt(String s) { + + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return 0; + } + } + private Object getDbField( + String player, String field) { + Main main = (Main) plugin; + return main.getDb().getField( + player, field); + } + + /** + * 从文本中提取第一个数字 + * "在线时间≥1小时" → 1 + * "破坏/放置方块100个" → 100 + */ + private int parseNumberFromText( + String text) { + java.util.regex.Matcher m = + java.util.regex.Pattern + .compile("(\\d+)") + .matcher(text); + if (m.find()) { + try { + return Integer.parseInt( + m.group(1)); + } catch (Exception e) { + return 0; + } + } + return 0; + } + public void onBlockPlaced( + String player, String material) { + String key = "PLACE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + setCounter(player, "PLACE:ALL", + getCounter(player, "PLACE:ALL") + + 1); + } + + public void onPlayerChat( + String player) { + String key = "CHAT"; + setCounter(player, key, + getCounter(player, key) + 1); + } + + private String formatMs(long ms) { + long sec = ms / 1000; + long min = sec / 60; + long hr = min / 60; + if (hr > 0) + return hr + "小时" + + (min % 60) + "分"; + if (min > 0) + return min + "分" + + (sec % 60) + "秒"; + return sec + "秒"; + } + + // ========== 数据结构 ========== + + public static class QuestFile { + public String displayName = ""; + public String fileName = ""; + public List details = + new ArrayList<>(); + public List rewards = + new ArrayList<>(); + public List conditions = + new ArrayList<>(); + + public boolean hasConditions() { + return conditions != null + && !conditions.isEmpty(); + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/RadioDownloadListener.java b/backup/20260605_152529/src/main/java/Sdf1_login/RadioDownloadListener.java new file mode 100644 index 0000000..d3e744f --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/RadioDownloadListener.java @@ -0,0 +1,144 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player + .PlayerResourcePackStatusEvent; + +import java.util.UUID; +import java.util.concurrent + .ConcurrentHashMap; + +public class RadioDownloadListener + implements Listener { + + private final Main plugin; + private final java.util.logging.Logger log; + + private final ConcurrentHashMap + failCount = new ConcurrentHashMap<>(); + private final ConcurrentHashMap + blocked = new ConcurrentHashMap<>(); + + private static final int MAX_FAILED = 3; + + public RadioDownloadListener(Main plugin) { + this.plugin = plugin; + this.log = plugin.getLogger(); + } + + public void onPlayerQuit(UUID uuid) { + failCount.remove(uuid); + blocked.remove(uuid); + } + + @EventHandler + public void onStatus( + PlayerResourcePackStatusEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + String status = + e.getStatus().name(); + + log.info("[Radio] === 资源包状态 ==="); + log.info("[Radio] 玩家: " + + p.getName()); + log.info("[Radio] UUID: " + uuid); + log.info("[Radio] 状态: " + + status); + log.info("[Radio] 原始状态枚举: " + + e.getStatus().getClass() + .getSimpleName() + + "." + + e.getStatus().name()); + + if (blocked.containsKey(uuid)) { + log.info("[Radio] 已熔断,跳过: " + + p.getName()); + return; + } + + switch (status) { + case "SUCCESS": + log.info("[Radio] " + + p.getName() + + " ✓ 资源包加载成功"); + failCount.remove(uuid); + blocked.remove(uuid); + break; + + case "ACCEPTED": + log.info("[Radio] " + + p.getName() + + " 客户端已接受," + + "等待下载..."); + break; + + case "DECLINED": + log.warning("[Radio] " + + p.getName() + + " ✗ 拒绝了资源包"); + log.warning("[Radio] " + + "可能原因: " + + "1.客户端设置禁用资源包" + + " 2.已缓存拒绝记录" + + " 3.SSL证书不匹配"); + handleFailure(p, uuid); + break; + + case "FAILED_DOWNLOAD": + log.warning("[Radio] " + + p.getName() + + " ✗ 下载失败"); + log.warning("[Radio] " + + "可能原因: " + + "1.URL无法访问" + + " 2.SSL证书错误" + + " 3.网络超时" + + " 4.文件不存在(404)"); + handleFailure(p, uuid); + break; + + default: + log.info("[Radio] " + + p.getName() + + " 未知状态: " + + status); + break; + } + } + + private void handleFailure(Player p, UUID uuid) { + int failNum = failCount.getOrDefault(uuid, 0) + 1; + failCount.put(uuid, failNum); + + log.warning("[Radio] " + p.getName() + + " 失败次数: " + failNum + "/" + MAX_FAILED); + + if (failNum >= MAX_FAILED) { + blocked.put(uuid, true); + log.severe("[Radio] " + p.getName() + + " 已达最大失败次数,停止重试," + + "玩家继续留在服务器"); + // 不踢出,只记录熔断,玩家正常游玩 + p.sendMessage("§e§l[Radio] 资源包加载失败" + + ",已取消下载资源包,部分画面音频可能出现问题" + + "\n§d§l若想继续下载,请退出重进。若持续失败,请上报管理员"); + + return; + } +// 打了包没测试 + p.sendMessage("§e§l[Radio] 下载失败," + + "重试... (" + failNum + "/" + MAX_FAILED + ")"); + + // 5秒后重试 + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline() && plugin.radio != null) { + plugin.radio.sendResourcePack(p); + } + }, 100L); + } +} \ No newline at end of file diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/RadioManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/RadioManager.java new file mode 100644 index 0000000..6fe5eda --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/RadioManager.java @@ -0,0 +1,1040 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.zip.*; +import java.util.Collections; +import org.bukkit.NamespacedKey; +import java.util.Set; + + +public class RadioManager { + + private final Main plugin; + private File radioDir; + private File oggDir; + private File packDir; + private BukkitTask mainRadioTask; + private final Map loginPlayers = + new ConcurrentHashMap<>(); + private String externalUrl = ""; + private int httpPort = 443; + private final Set pendingPack = + Collections.newSetFromMap( + new ConcurrentHashMap<>()); + private String lastPlayedSound = ""; + + private int radioIndex = 0; + + // ========== 登录阶段推送(管理员手动触发) ========== + public void startLoginRadio(Player p) { + loginPlayers.put(p.getName(), true); + } + + public void stopLoginRadio(Player p) { + loginPlayers.remove(p.getName()); + } + + public void stopAllLoginRadio() { + loginPlayers.clear(); + } + + private final List mainFiles = new ArrayList<>(); + private final List loginFiles = new ArrayList<>(); + + public RadioManager(Main plugin) { + this.plugin = plugin; + this.radioDir = new File( + plugin.getDataFolder(), "radio"); + this.oggDir = new File( + plugin.getDataFolder(), "radio/ogg"); + this.packDir = new File( + plugin.getDataFolder(), "radiopack"); + radioDir.mkdirs(); + oggDir.mkdirs(); + packDir.mkdirs(); + loadRadioConfig(); + } + // ========== 读取 radio_config.txt ========== + private void loadRadioConfig() { + File cfg = new File( + plugin.getDataFolder(), + "radio_config.txt"); + + // 不存在则创建默认配置 + if (!cfg.exists()) { + createRadioConfig(cfg); + plugin.getLogger().info( + "[Radio] 已生成 radio_config.txt"); + } + + // 读取 + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(cfg), + java.nio.charset + .StandardCharsets + .UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("#") + || line.trim().isEmpty()) + continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String key = line.substring(0, eq) + .trim(); + String val = line.substring(eq + 1) + .trim(); + if (key.equals("resource-pack-url")) { + externalUrl = val; + } else if (key.equals("http-port")) { + try { + httpPort = Integer.parseInt( + val); + } catch (NumberFormatException ignored) { + } + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 读取radio_config.txt失败: " + + e.getMessage()); + } + + // 443是HTTPS端口,内嵌HTTP不能用 + if (httpPort == 443) httpPort = 8080; + + plugin.getLogger().info( + "[Radio] 配置: externalUrl=[" + + externalUrl + "] httpPort=" + + httpPort); + } + + private void createRadioConfig(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + java.nio.charset + .StandardCharsets + .UTF_8))) { + pw.println("# ============ Sdf1 Radio Config ============"); + pw.println("#"); + pw.println("# resource-pack-url"); + pw.println("# 填外部地址(HTTP或HTTPS均可)"); + pw.println("# 例: https://example.com/radiopack.zip"); + pw.println("# 留空 = 使用内置HTTP服务"); + pw.println("#"); + pw.println("# http-port"); + pw.println("# 内置HTTP端口(仅resource-pack-url为空时生效)"); + pw.println("#"); + pw.println("# ============ Settings ==============="); + pw.println(); + pw.println("resource-pack-url="); + pw.println("http-port=8080"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 创建radio_config.txt失败: " + + e.getMessage()); + } + } + + // ========== 初始化 ========== + public void init() { + loadRadioConfig(); + convertNonOgg(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); // 启动HTTP + plugin.getLogger().info( + "[Radio] 初始化完成: 全服=" + + mainFiles.size() + + " 登录=" + loginFiles.size()); + } + + + // ========== 转换非ogg文件 ========== + private void convertNonOgg() { + convertDir(radioDir); + convertDir(new File(radioDir, "login")); + convertDir(new File(radioDir, "reg")); + } + + private void convertDir(File src) { + if (!src.exists() || !src.isDirectory()) return; + File[] files = src.listFiles((d, n) -> { + String l = n.toLowerCase(); + return l.endsWith(".mp3") + || l.endsWith(".wav") + || l.endsWith(".flac"); + }); + if (files == null) return; + + for (File f : files) { + String oggName = f.getName() + .replaceAll("\\.(mp3|wav|flac)$", + ".ogg"); + File oggFile = new File(src, oggName); + if (oggFile.exists() + && oggFile.lastModified() + >= f.lastModified()) { + continue; + } + // 调用ffmpeg转换 + if (convertWithFfmpeg(f, oggFile)) { + plugin.getLogger().info( + "[Radio] 转换成功: " + + f.getName()); + } else { + plugin.getLogger().warning( + "[Radio] 转换失败: " + + f.getName() + + " (需要安装ffmpeg)"); + } + } + } + + private boolean convertWithFfmpeg( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-c:a", "libvorbis", + "-q:a", "5", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean finished = + p.waitFor(30, TimeUnit.SECONDS); + if (!finished) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + return false; + } + } + + // ========== 加载ogg文件列表 ========== + private void loadAllOggs() { + mainFiles.clear(); + loginFiles.clear(); + loadOggsFlat(radioDir, mainFiles); + loadOggsFlat( + new File(radioDir, "login"), + loginFiles); + loadOggsFlat( + new File(radioDir, "reg"), + loginFiles); + } + + private void loadOggsFlat( + File dir, List list) { + if (!dir.exists() || !dir.isDirectory()) + return; + File[] files = dir.listFiles( + (d, n) -> n.toLowerCase() + .endsWith(".ogg")); + if (files == null) return; + Arrays.sort(files); + for (File f : files) list.add(f); + } + /** + * 解析 pack.png:优先读取插件目录, + * 没有则搜索图片并用ffmpeg转码 + */ + private File resolvePackIcon() { + File dataDir = plugin.getDataFolder(); + File packPng = new File(dataDir, "pack.png"); + + if (packPng.exists() + && packPng.length() > 0) { + plugin.getLogger().info( + "[Radio] 找到 pack.png (" + + packPng.length() + + " bytes)"); + return packPng; + } + + // 搜索可转换的图片文件 + String[] exts = {"png", "jpg", "jpeg", + "bmp", "webp", "tiff", "gif"}; + File[] found = dataDir.listFiles( + (dir, name) -> { + String low = name.toLowerCase(); + for (String ext : exts) { + if (low.endsWith("." + ext) + && !name.equals( + "pack.png")) { + return true; + } + } + return false; + }); + + if (found != null && found.length > 0) { + File src = found[0]; + plugin.getLogger().info( + "[Radio] 找到图片: " + + src.getName() + + " → 转码为 pack.png"); + if (convertToPackPng(src, packPng)) { + return packPng; + } + plugin.getLogger().warning( + "[Radio] 转码失败"); + return null; + } + + plugin.getLogger().warning( + "[Radio] 未找到 pack.png 或可转换图片"); + return null; + } + + /** + * ffmpeg 将任意图片转为 256x256 PNG + */ + private boolean convertToPackPng( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-vf", + "scale='min(256,iw)'" + + ":'min(256,ih)':" + + "force_original_aspect_ratio" + + "=decrease," + + "pad=256:256:" + + "(ow-iw)/2:" + + "(oh-ih)/2:" + + "color=0x00000000", + "-c:v", "png", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean ok = p.waitFor( + 30, TimeUnit.SECONDS); + if (!ok) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] ffmpeg转码失败: " + + e.getMessage()); + return false; + } + } + + // ========== 资源包构建 ========== + private void buildResourcePack() { + deleteDir(packDir); + packDir.mkdirs(); + + List allOggs = new ArrayList<>(); + collectOggs(radioDir, allOggs); + + if (allOggs.isEmpty()) { + plugin.getLogger().warning( + "[Radio] radio/ 目录下" + + "无 .ogg 文件,跳过资源包"); + return; + } + + // 正确路径:assets/minecraft/sounds/custom/ + File soundsDir = new File(packDir, + "assets/minecraft/sounds/custom"); + soundsDir.mkdirs(); + + // 生成 sounds.json + StringBuilder json = + new StringBuilder("{\n"); + for (int i = 0; i < allOggs.size(); i++) { + File ogg = allOggs.get(i); + String simpleName = + ogg.getName() + .replace(".ogg", ""); + File dest = new File(soundsDir, + simpleName + ".ogg"); + try { + Files.copy(ogg.toPath(), + dest.toPath(), + StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + // ignore + } + // 音频事件名: custom.xxx + // 文件路径: custom/xxx + json.append(" \"custom.") + .append(simpleName) + .append("\": {\n"); + json.append(" \"sounds\": [{\n"); + json.append(" \"name\":" + + " \"custom/") + .append(simpleName) + .append("\",\n"); + json.append( + " \"stream\": true\n"); + json.append(" }]\n"); + json.append(" }"); + if (i < allOggs.size() - 1) + json.append(","); + json.append("\n"); + } + json.append("}"); + + // sounds.json 放在 assets/minecraft/ 下 + try { + Files.write(new File(packDir, + "assets/minecraft/sounds.json") + .toPath(), + json.toString() + .getBytes("UTF-8")); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] sounds.json写入失败"); + } +// sounds.json 写入后... + +// ===== 新增:打包 pack.png 作为资源包图标 ===== + File iconPng = resolvePackIcon(); + if (iconPng != null && iconPng.exists()) { + try { + Files.copy(iconPng.toPath(), + new File(packDir, "pack.png") + .toPath(), + StandardCopyOption + .REPLACE_EXISTING); + plugin.getLogger().info( + "[Radio] pack.png 已加入资源包"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] pack.png复制失败: " + + e.getMessage()); + } + } + + // pack.mcmeta(pack_format=54) + try { + Files.write(new File(packDir, + "pack.mcmeta").toPath(), + ("{\n \"pack\": {\n" + + " \"pack_format\": 54,\n" + + " \"description\":" + + " \"§d§l草原探险服务器资源包,请勿拒绝资源包\"\n }\n}") + .getBytes("UTF-8")); + } catch (Exception e) { + // ignore + } + + try { + File zipFile = new File( + plugin.getDataFolder(), + "radiopack.zip"); + packZip(packDir, zipFile); + plugin.getLogger().info( + "[Radio] 资源包生成: " + + allOggs.size() + " 个音频"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] zip打包失败: " + + e.getMessage()); + } + } + + private void deleteDir(File dir) { + if (!dir.exists()) return; + File[] files = dir.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) deleteDir(f); + else f.delete(); + } + } + dir.delete(); + } + + private void collectOggs( + File dir, List list) { + File[] files = dir.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) + collectOggs(f, list); + else if (f.getName().endsWith(".ogg")) + list.add(f); + } + } + + private void packZip(File root, + File zipFile) throws Exception { + try (ZipOutputStream zos = + new ZipOutputStream( + new FileOutputStream( + zipFile))) { + zipDir(root, root, zos); + } + } + + private void zipDir(File root, + File current, + ZipOutputStream zos) + throws Exception { + File[] files = current.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) { + zipDir(root, f, zos); + } else { + String path = root.toURI() + .relativize(f.toURI()) + .getPath(); + zos.putNextEntry(new ZipEntry(path)); + zos.write(Files.readAllBytes( + f.toPath())); + zos.closeEntry(); + } + } + } + + // ========== 资源包发送 ========== + + /** + * 发送资源包(带限流,不禁重试) + */ + private final Map lastSendTime = + new ConcurrentHashMap<>(); + + public void sendResourcePack(Player p) { + UUID uuid = p.getUniqueId(); + + // 最短间隔 5秒(不是10秒的全锁死) + Long last = lastSendTime.get(uuid); + if (last != null + && System.currentTimeMillis() - last + < 5000L) { + plugin.getLogger().info( + "[Radio] 跳过(5秒内): " + + p.getName()); + return; + } + lastSendTime.put(uuid, + System.currentTimeMillis()); + + String url = getResourcePackUrl(); + if (url == null || url.isEmpty()) { + plugin.getLogger().warning( + "[Radio] URL为空,跳过: " + + p.getName()); + return; + } + + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) { + plugin.getLogger().warning( + "[Radio] radiopack.zip不存在," + + "跳过: " + p.getName()); + return; + } + + try { + byte[] data = + Files.readAllBytes(zip.toPath()); + String hash = sha1(data); + long fileSize = zip.length(); + + plugin.getLogger().info( + "[Radio] 发送: " + p.getName() + + " url=" + url + + " size=" + fileSize + + " hash=" + hash); + + sendPackWithHash(p, url, hash); + + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] 发送异常: " + + p.getName()); + e.printStackTrace(); + } + } + + + + private void sendPackWithHash( + Player p, + String url, + String hash) { + + UUID uuid = UUID.fromString( + "a1b2c3d4-e5f6-7890-abcd-" + + "ef1234567890"); + + // 尝试5参 + Component + try { + Object component = + net.kyori.adventure.text + .Component.text( + "§d§l请下载自定义资源包"); + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + net.kyori.adventure + .text.Component + .class, + boolean.class) + .invoke( + p, uuid, url, + hash, component, + true); + plugin.getLogger().info( + "[Radio] 成功(5参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试5参 + String描述 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + String.class, + boolean.class) + .invoke( + p, uuid, url, + hash, + "Sdf1 Radio", + true); + plugin.getLogger().info( + "[Radio] 成功(5参String): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试3参 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class) + .invoke( + p, uuid, url, + hash); + plugin.getLogger().info( + "[Radio] 成功(3参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // ★ 全部失败 → 通知玩家 + 熔断 + plugin.getLogger().severe( + "[Radio] 所有签名失败: " + + p.getName() + + " 客户端版本: " + + p.getProtocolVersion()); + p.sendMessage( + "§e§l[Radio] §f资源包发送失败," + + "§7部分功能可能异常"); + p.sendMessage( + "§7可尝试: §e/rejoin §7或重启客户端"); + } + + + + private void sendPackFromLocal( + Player p, String url) { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) return; + try { + byte[] data = + Files.readAllBytes( + zip.toPath()); + String hash = sha1(data); + sendPackWithHash(p, url, hash); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 降级发送失败"); + } + } + + // 从URL下载字节 + private byte[] downloadBytes(String urlStr) + throws Exception { + java.net.URL url = + new java.net.URL(urlStr); + java.net.HttpURLConnection conn = + (java.net.HttpURLConnection) + url.openConnection(); + conn.setConnectTimeout(10000); + conn.setReadTimeout(30000); + try (java.io.InputStream is = + conn.getInputStream()) { + return is.readAllBytes(); + } + } + + // SHA-1计算 + private String sha1(byte[] data) + throws Exception { + java.security.MessageDigest md = + java.security.MessageDigest + .getInstance("SHA-1"); + md.update(data); + byte[] hash = md.digest(); + StringBuilder sb = + new StringBuilder(); + for (byte b : hash) + sb.append(String.format( + "%02x", b)); + return sb.toString(); + } + + public void clearPending(UUID uuid) { + lastSendTime.remove(uuid); + } + + + + // ========== 内嵌HTTP服务 ========== + private com.sun.net.httpserver.HttpServer + httpServer; + + private void startHttpServer() { + // 如果配置了外部地址则不启动 + if (plugin.getConfig2() != null + && !plugin.getConfig2() + .broadcastServerUrl + .isEmpty()) { + return; + } + try { + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + httpServer = + com.sun.net.httpserver + .HttpServer.create( + new java.net + .InetSocketAddress( + port), 0); + httpServer.createContext( + "/radiopack.zip", + exchange -> { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (zip.exists()) { + byte[] data = + Files.readAllBytes( + zip.toPath()); + exchange.getResponseHeaders() + .set("Content-Type", + "application/zip"); + exchange.getResponseHeaders() + .set("Access-Control" + + "-Allow-Origin", + "*"); + exchange.sendResponseHeaders( + 200, data.length); + exchange.getResponseBody() + .write(data); + exchange.getResponseBody() + .close(); + } else { + byte[] empty = + "not found".getBytes(); + exchange.sendResponseHeaders( + 404, empty.length); + exchange.getResponseBody() + .write(empty); + exchange.getResponseBody() + .close(); + } + }); + httpServer.setExecutor(null); + httpServer.start(); + plugin.getLogger().info( + "[Radio] HTTP服务: http://0.0.0.0:" + + port + "/radiopack.zip"); + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] HTTP启动失败: " + + e.getMessage()); + } + } + + private void stopHttpServer() { + if (httpServer != null) { + httpServer.stop(0); + httpServer = null; + } + } + + public String getResourcePackUrl() { + // 有外部配置 → 随机生成URL + if (externalUrl != null + && !externalUrl.trim().isEmpty()) { + return generateRandomUrl(externalUrl); + } + // 无配置 → 用内置HTTP + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + return "http://127.0.0.1:" + + port + "/radiopack.zip"; + } + + + /** + * 从配置的域名列表中随机选一个 + * 生成随机、带时间戳的完整资源包URL + * + * 规则: + * *.example.com + * → 随机子域+时间戳 + * 例: a3f7k2.example.com/pack.zip?t=xxx&v=b9c1e4 + * + * cdn.example.com (无*) + * → 直接使用+时间戳 + * 例: cdn.example.com/pack.zip?t=xxx + */ + private String generateRandomUrl( + String raw) { + String[] parts = raw.split("\\|"); + String pick = + parts[(int)(Math.random() + * parts.length)].trim(); + + // 去掉协议 https:// http:// + String cleaned = pick + .replaceFirst("^https?://", ""); + // 去掉路径 /path/a?v=1 + if (cleaned.contains("/")) { + cleaned = cleaned.substring( + 0, cleaned.indexOf('/')); + } + + // 随机子域 + String hex = Long.toHexString( + Double.doubleToLongBits( + Math.random())); + String rand = hex.length() > 6 + ? hex.substring(2, 8) : hex; + rand = rand.replaceAll( + "[^a-z0-9]", "x"); + + String host; + if (cleaned.startsWith("*")) { + host = rand + + cleaned.substring(1); + } else { + host = cleaned; + } + + String ts = String.valueOf( + System.currentTimeMillis()); + String token = Integer.toHexString( + (int)(Math.random() + * 0xFFFFFF)); + + String url = "https://" + host + + "/radiopack.zip" + + "?t=" + ts + + "&v=" + token; + + plugin.getLogger().info( + "[Radio] 生成URL: " + + pick + " → " + url); + return url; + } + + + + // ========== 全服广播 ========== + public void startMainRadio() { + stopMainRadio(); + if (mainFiles.isEmpty()) { + plugin.getLogger().warning( + "[Radio] 无 .ogg 文件"); + return; + } + plugin.getLogger().info( + "[Radio] 启动全服广播 (" + + mainFiles.size() + " 个文件)"); + mainRadioTask = new BukkitRunnable() { + @Override + public void run() { + playNext(); + } + }.runTaskTimer(plugin, 100L, 2400L); + } + + private void playNext() { + if (mainFiles.isEmpty()) return; + File file = mainFiles.get( + radioIndex % mainFiles.size()); + String name = file.getName() + .replace(".ogg", ""); + lastPlayedSound = "custom." + name; + String key = "minecraft:" + lastPlayedSound; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + radioIndex++; + } + + // ========== 按名称播放 ========== + public void playToAll(String name) { + String key = findSongKey(name); + if (key == null) return; + lastPlayedSound = "custom." + name; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + } + + + public void playToPlayer( + Player p, String name) { + String key = findSongKey(name); + if (key == null) { + p.sendMessage("§c未找到: " + name); + return; + } + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + + public void playToLoginPlayers(String name) { + String key = findSongKey(name); + if (key == null) return; + for (String pName + : loginPlayers.keySet()) { + Player lp = Bukkit.getPlayer(pName); + if (lp != null && lp.isOnline()) { + lp.playSound(lp.getLocation(), + key, + SoundCategory.RECORDS, + 1.5f, 1.0f); + } + } + } + + + private String findSongKey(String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + return "minecraft:custom." + name; + } + } + plugin.getLogger().warning( + "[Radio] 未找到歌曲: " + name); + return null; + } + + + public boolean hasSong(String name) { + return findSongKey(name) != null; + } + + // ========== 手动播放 ========== + public boolean playSpecific( + Player sender, String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + String key = "sdf1radio:" + name; + for (Player p + : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), + key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + + name); + return true; + } + } + if (sender != null) + sender.sendMessage("§c未找到: " + name); + return false; + } + public void stopMainRadio() { + if (mainRadioTask != null) { + mainRadioTask.cancel(); + mainRadioTask = null; + } + if (lastPlayedSound != null + && !lastPlayedSound.isEmpty()) { + for (Player p : Bukkit.getOnlinePlayers()) { + p.stopSound(lastPlayedSound, + SoundCategory.RECORDS); + } + lastPlayedSound = ""; + } + } + +// 在 init() 或 registerEvents 中保存引用: +// this.listener = new RadioDownloadListener(plugin); +private RadioDownloadListener radioListener; + + + + // ========== 重载 ========== + public void reload() { + stopMainRadio(); + stopAllLoginRadio(); + stopHttpServer(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); + loadRadioConfig(); + } +} \ No newline at end of file diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/RefundRecord.java b/backup/20260605_152529/src/main/java/Sdf1_login/RefundRecord.java new file mode 100644 index 0000000..c0a3070 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/RefundRecord.java @@ -0,0 +1,62 @@ +package Sdf1_login; + +import org.bukkit.Material; +import java.util.UUID; + +public class RefundRecord { + public final UUID playerUUID; + public final String playerName; + public final String itemId; + public final String itemName; + public final Material material; + public final Material shulkerMaterial; + public int amount; + public int totalPaid; + public final long timestamp; + + // 普通购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, int amount, + int totalPaid, long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = null; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + // 盒装购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, + Material shulkerMat, + int amount, int totalPaid, + long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = shulkerMat; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + public boolean isExpired() { + return System.currentTimeMillis() + - timestamp > 300000L; + } + + public int getSecondsLeft() { + long left = 300000L + - (System.currentTimeMillis() + - timestamp); + return (int) Math.max(0, left / 1000); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/RiskControlManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/RiskControlManager.java new file mode 100644 index 0000000..4ddedcc --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/RiskControlManager.java @@ -0,0 +1,248 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 风控管理器:检测异常登录行为,记录尝试次数,触发安全报警 + */ +public class RiskControlManager { + + private final Main plugin; + + // 每个玩家名 -> 连续失败次数 + private final Map failAttempts = + new ConcurrentHashMap<>(); + + // 每个IP -> 关联的可疑玩家列表 + private final Map> ipSuspicious = + new ConcurrentHashMap<>(); + + // 每个玩家 -> 上次验证时间(用于冷却) + private final Map verifyCooldown = + new ConcurrentHashMap<>(); + + // 配置项 + private static final int MAX_FAIL_ATTEMPTS = 5; + private static final int MAX_ACCOUNTS_PER_IP = 5; + private static final long COOLDOWN_MS = 60000L; + + public RiskControlManager(Main plugin) { + this.plugin = plugin; + } + + /** + * 记录一次失败的登录尝试 + * + * @param playerName 玩家名 + * @return 剩余尝试次数 + */ + public int recordFailAttempt(String playerName) { + int current = failAttempts + .getOrDefault(playerName, 0) + 1; + failAttempts.put(playerName, current); + return Math.max(0, MAX_FAIL_ATTEMPTS - current); + } + + /** + * 重置指定玩家的失败计数(登录成功时调用) + */ + public void resetFailAttempts(String playerName) { + failAttempts.remove(playerName); + } + + /** + * 获取指定玩家的失败次数 + */ + public int getFailAttempts(String playerName) { + return failAttempts + .getOrDefault(playerName, 0); + } + + /** + * 判断玩家是否已被锁定(失败次数达到上限) + */ + public boolean isLocked(String playerName) { + return failAttempts + .getOrDefault(playerName, 0) + >= MAX_FAIL_ATTEMPTS; + } + + /** + * 检查IP是否达到注册上限 + * 直接查数据库,不依赖 DatabaseManager 的方法 + */ + public boolean canRegisterFromIP(String ip) { + if (ip == null || ip.isEmpty()) return true; + try { + java.sql.Connection conn = + plugin.getDb().getDb(); + if (conn == null) return true; + java.sql.PreparedStatement ps = + conn.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address = ?"); + ps.setString(1, ip); + java.sql.ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count < 5; + } catch (Exception e) { + e.printStackTrace(); + return true; + } + } + + + /** + * 记录IP关联的可疑行为 + */ + public void recordSuspiciousIP(String ip, + String playerName) { + if (ip == null || ip.isEmpty()) return; + ipSuspicious + .computeIfAbsent(ip, + k -> ConcurrentHashMap.newKeySet()) + .add(playerName); + } + + /** + * 获取某IP关联的所有可疑玩家 + */ + public Set getSuspiciousAccounts(String ip) { + return ipSuspicious + .getOrDefault(ip, Collections.emptySet()); + } + + /** + * 检查是否在验证冷却期内 + */ + public boolean isOnCooldown(String playerName) { + Long last = verifyCooldown.get(playerName); + if (last == null) return false; + return System.currentTimeMillis() - last + < COOLDOWN_MS; + } + + /** + * 设置验证冷却时间 + */ + public void setCooldown(String playerName) { + verifyCooldown.put(playerName, + System.currentTimeMillis()); + } + + /** + * 当检测到可疑登录时触发报警 + * - 全服广播 + * - 有邮箱则发邮件 + * - 记录到数据库 + */ + public void triggerAlert(Player player, + String suspiciousIP) { + String name = player.getName(); + + // 记录到数据库 + plugin.getDb().recordSecurityAlert( + name, suspiciousIP); + + // 记录IP可疑行为 + recordSuspiciousIP(suspiciousIP, name); + + // 全服广播 + String serverMsg = "§c§l[安全警告] §e" + name + + " §f的账号疑似遭到 §c" + suspiciousIP + + " §f的盗号尝试, 请各位玩家注意账号安全!"; + Bukkit.broadcastMessage(serverMsg); + + // 有邮箱则发邮件 + String emailAddr = (String) plugin.getDb() + .getField(name, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String subject = + "[安全警告] 您的账号疑似遭到盗号尝试"; + String body = "尊敬的玩家 " + name + + ":\n\n" + + "您的账号在 " + + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()) + + " 疑似遭到 IP: " + suspiciousIP + + " 的盗号尝试。\n\n" + + "建议您:\n" + + "1. 立即修改密码" + + "(使用 /sdf1_login pw)\n" + + "2. 如非本人操作," + + "请联系管理员处理\n\n" + + "--Sdf1_login 安全系统"; + plugin.getEmail().sendTempPassword( + emailAddr, name, body); + } + } + + /** + * 处理登录失败时的风控逻辑 + * + * @param player 玩家 + * @param suspiciousIP 可疑来源IP + * @return true=已被锁定(应踢出), false=仍可继续尝试 + */ + public boolean handleLoginFailure(Player player, + String suspiciousIP) { + String name = player.getName(); + int remaining = recordFailAttempt(name); + + if (remaining <= 0) { + // 达到上限,触发报警 + triggerAlert(player, suspiciousIP); + player.kickPlayer( + "§c§l[风控] §f登录失败次数过多," + + " 账号已被临时锁定, 请联系管理员"); + return true; + } + + // 未达上限,提示剩余次数 + player.sendMessage( + "§c§l[风控] §f密码错误! " + + "剩余" + remaining + "次机会"); + return false; + } + + /** + * 登录成功后重置风控状态 + */ + public void onLoginSuccess(String playerName) { + resetFailAttempts(playerName); + } + + /** + * 清理过期的冷却记录(可由定时任务调用) + */ + public void cleanup() { + long now = System.currentTimeMillis(); + verifyCooldown.entrySet().removeIf( + e -> now - e.getValue() > COOLDOWN_MS * 10); + } + + /** + * 获取所有玩家的失败尝试记录(调试用) + */ + public Map getAllFailAttempts() { + return Collections.unmodifiableMap(failAttempts); + } + + /** + * 清除所有风控数据(管理员调试用) + */ + public void clearAll() { + failAttempts.clear(); + ipSuspicious.clear(); + verifyCooldown.clear(); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/SalesStatsManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/SalesStatsManager.java new file mode 100644 index 0000000..9d06a22 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/SalesStatsManager.java @@ -0,0 +1,170 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class SalesStatsManager { + + private final Main plugin; + + private final Map sessionSales = + new ConcurrentHashMap<>(); + private int sessionRevenue = 0; + private int sessionOrders = 0; + private int sessionItems = 0; + private int sessionRefunds = 0; + private int sessionRefundAmount = 0; + private long sessionStart = System.currentTimeMillis(); + + // 状态 + private boolean waitingForEmpty = false; + private boolean hasReported = false; + private BukkitRunnable waitTask = null; + + public SalesStatsManager(Main plugin) { + this.plugin = plugin; + startMonitor(); + } + + // ★ 每10秒检测一次 + private void startMonitor() { + new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 有玩家在线 → 重置等待状态 + if (waitingForEmpty) { + // 刚有人进来了,清除已汇报标记 + // 下次没人时会重新汇报 + } + waitingForEmpty = false; + hasReported = false; + cancelWaitTask(); + } else if (!waitingForEmpty && !hasReported) { + // 没人了,且还没汇报过 → 开始1分钟倒计时 + waitingForEmpty = true; + startWaitCountdown(); + } + } + }.runTaskTimer(plugin, 200L, 200L); + } + + // ★ 1分钟等待 + private void startWaitCountdown() { + cancelWaitTask(); + waitTask = new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 等待期间有人进来 → 取消,等下次没人 + waitingForEmpty = false; + hasReported = false; + waitTask = null; + cancel(); + return; + } + + // 1分钟没人 → 汇报一次 + if (!hasReported) { + hasReported = true; + report(); + } + waitingForEmpty = false; + waitTask = null; + cancel(); + } + }; + waitTask.runTaskLater(plugin, 1200L); // 60秒 + } + + private void cancelWaitTask() { + if (waitTask != null) { + waitTask.cancel(); + waitTask = null; + } + } + + // ===== 记录 ===== + + public void recordSale(String itemName, int price) { + sessionSales.merge(itemName, 1, Integer::sum); + sessionRevenue += price; + sessionOrders++; + sessionItems++; + } + + public void recordRefund(int amount) { + sessionRefunds++; + sessionRefundAmount += amount; + } + + // ===== 汇报 ===== + + public void report() { + if (sessionItems == 0 && sessionRefunds == 0) { + // plugin.getLogger().info("[Sales] 无数据,跳过"); + return; + } + + long minutes = + (System.currentTimeMillis() - sessionStart) + / 60000; + if (minutes < 1) minutes = 1; + + StringBuilder sb = new StringBuilder(); + sb.append("§6§l========== 销量汇总 =========="); + sb.append("\n§7时长: §f").append(minutes).append("分钟"); + sb.append("\n§7订单: §f").append(sessionOrders).append("笔"); + sb.append("\n§7商品: §f").append(sessionItems).append("件"); + sb.append("\n§7成交额: §6").append(sessionRevenue).append("枚"); + sb.append("\n§7退款: §c").append(sessionRefunds) + .append("次(§c").append(sessionRefundAmount) + .append("枚§7)"); + sb.append("\n§6§l------------------------------"); + + if (!sessionSales.isEmpty()) { + sb.append("\n§7销量排行:"); + List> sorted = + new ArrayList<>(sessionSales.entrySet()); + sorted.sort((a, b) -> + b.getValue() - a.getValue()); + int rank = 1; + for (Map.Entry e : sorted) { + if (rank > 10) break; + sb.append("\n§e").append(rank).append(". ") + .append(e.getKey()) + .append(" §7x").append(e.getValue()); + rank++; + } + } + sb.append("\n§6§l=============================="); + + String msg = sb.toString(); + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.isOp()) { + for (String line : msg.split("\n")) + p.sendMessage(line); + } + } + plugin.getLogger().info("[Sales] 汇报完成"); + resetSession(); + } + + public void resetSession() { + sessionSales.clear(); + sessionRevenue = 0; + sessionOrders = 0; + sessionItems = 0; + sessionRefunds = 0; + sessionRefundAmount = 0; + sessionStart = System.currentTimeMillis(); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ShopCategory.java b/backup/20260605_152529/src/main/java/Sdf1_login/ShopCategory.java new file mode 100644 index 0000000..715e9ad --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ShopCategory.java @@ -0,0 +1,32 @@ +package Sdf1_login; + +import java.util.ArrayList; +import java.util.List; + +public class ShopCategory { + private final String name; + private final String fileName; + private final List items; + + public ShopCategory(String name, String fileName) { + this.name = name; + this.fileName = fileName; + this.items = new ArrayList<>(); + } + + public String getName() { return name; } + public String getFileName() { return fileName; } + public List getItems() { return items; } + + public ShopItem getItem(String id) { + for (ShopItem item : items) { + if (item.getId().equalsIgnoreCase(id)) + return item; + } + return null; + } + + public void addItem(ShopItem item) { + items.add(item); + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ShopItem.java b/backup/20260605_152529/src/main/java/Sdf1_login/ShopItem.java new file mode 100644 index 0000000..d336c7c --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ShopItem.java @@ -0,0 +1,49 @@ +package Sdf1_login; + +import org.bukkit.Material; + +public class ShopItem { + private final String id; + private final String displayName; + private final Material material; + private final int buyPrice; + private final int sellPrice; + private int stock; + private int hourlySales; + private int totalSales; + public ShopItem(String id, String displayName, + Material material, int buyPrice, + int sellPrice, int stock, + int hourlySales, int totalSales) { + this.id = id; + this.displayName = displayName; + this.material = material; + this.buyPrice = buyPrice; + this.sellPrice = sellPrice; + this.stock = stock; + this.hourlySales = hourlySales; + this.totalSales = totalSales; + } + + public int getEffectiveSellPrice() { + if (sellPrice <= 0) return 0; + return sellPrice; + } + + public String getId() { return id; } + public String getDisplayName() { return displayName; } + public Material getMaterial() { return material; } + public int getBuyPrice() { return buyPrice; } + public int getSellPrice() { return sellPrice; } + public int getStock() { return stock; } + public int getHourlySales() { return hourlySales; } + public int getTotalSales() { return totalSales; } + + public void setStock(int s) { this.stock = s; } + public void setHourlySales(int s) { this.hourlySales = s; } + public void setTotalSales(int s) { this.totalSales = s; } + public void addSales(int n) { + this.hourlySales += n; + this.totalSales += n; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/ShopManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/ShopManager.java new file mode 100644 index 0000000..cdcc045 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/ShopManager.java @@ -0,0 +1,2610 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Base64; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.inventory.meta.PotionMeta; + + + + +public class ShopManager implements Listener { + + private final Main plugin; + private final List categories = + new ArrayList<>(); + private final List refundRecords = + new ArrayList<>(); + private final Map activeDiscount = + new HashMap<>(); + private final Map viewCat = + new HashMap<>(); + private final Map viewPage = + new HashMap<>(); + private final Map> shoppingCarts = + new HashMap<>(); + private final Set cartModeEnabled = + new HashSet<>(); + private final java.util.Random rng = + new java.util.Random(); + private final Map> pendingCheckout = + new HashMap<>(); + private final Set couponListening = + new HashSet<>(); + private final Map pendingCouponCode = + new HashMap<>(); + // ===== 物品NBT数据 ===== + private final Map itemNbtData = + new HashMap<>(); + private final Map itemStackCache = + new HashMap<>(); + // 替换字段 + private final Map couponApplyTime = new HashMap<>(); + + + + private static final Material[] SHULKER_COLORS = { + Material.SHULKER_BOX, + Material.WHITE_SHULKER_BOX, + Material.ORANGE_SHULKER_BOX, + Material.MAGENTA_SHULKER_BOX, + Material.LIGHT_BLUE_SHULKER_BOX, + Material.YELLOW_SHULKER_BOX, + Material.LIME_SHULKER_BOX, + Material.PINK_SHULKER_BOX, + Material.GRAY_SHULKER_BOX, + Material.LIGHT_GRAY_SHULKER_BOX, + Material.CYAN_SHULKER_BOX, + Material.PURPLE_SHULKER_BOX, + Material.BLUE_SHULKER_BOX, + Material.BROWN_SHULKER_BOX, + Material.GREEN_SHULKER_BOX, + Material.RED_SHULKER_BOX, + Material.BLACK_SHULKER_BOX + }; + private static final Map EFFECT_MAP = new HashMap<>(); + static { + // ===== 基础效果 ===== + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("速度", PotionEffectType.SPEED); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); // 1.21+ 改名 + EFFECT_MAP.put("治疗", PotionEffectType.INSTANT_HEALTH); // 1.21+ 改名 + EFFECT_MAP.put("治愈", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("伤害", PotionEffectType.INSTANT_DAMAGE); // 1.21+ 改名 + EFFECT_MAP.put("剧毒", PotionEffectType.POISON); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("回复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("再生", PotionEffectType.REGENERATION); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性", PotionEffectType.RESISTANCE); // 1.21+ 改名 + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); // 1.21+ 改名 + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); // 1.21+ 改名 + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); // ★ 补充 + EFFECT_MAP.put("迟缓", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("不祥", PotionEffectType.UNLUCK); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("生命加成", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("海豚关怀", PotionEffectType.DOLPHINS_GRACE); + EFFECT_MAP.put("跳跃", PotionEffectType.JUMP_BOOST); + // ★ 神龟药水是双效果,特殊处理,这里只做名称匹配入口 + EFFECT_MAP.put("神龟", PotionEffectType.RESISTANCE); + } + + public static class CartItem { + String itemId; + String displayName; + Material material; + int unitPrice; + int quantity; + } + + + private static final int PAGE_SIZE = 28; + private static final int[] ITEM_SLOTS = { + 10,11,12,13,14,15,16, + 19,20,21,22,23,24,25, + 28,29,30,31,32,33,34, + 37,38,39,40,41,42,43 + }; + + public ShopManager(Main plugin) { + this.plugin = plugin; + loadCategories(); + startHourlyTask(); + loadItemNbtData(); + startCleanupTask(); + } + // ===== 切换购物车模式 ===== + // ===== 切换购物车模式 ===== + public boolean toggleCartMode(Player p) { + UUID uuid = p.getUniqueId(); + if (cartModeEnabled.contains(uuid)) { + cartModeEnabled.remove(uuid); + p.sendMessage("§7购物车模式已关闭,恢复正常购买"); + return false; + } else { + cartModeEnabled.add(uuid); + p.sendMessage("§a购物车模式已开启,点击商品加入购物车"); + return true; + } + } + + public boolean isCartMode(Player p) { + return cartModeEnabled.contains(p.getUniqueId()); + } + private String itemToBase64(ItemStack item) { + try { + ByteArrayOutputStream os = + new ByteArrayOutputStream(); + BukkitObjectOutputStream bos = + new BukkitObjectOutputStream(os); + bos.writeObject(item); + bos.close(); + return Base64.getEncoder() + .encodeToString(os.toByteArray()); + } catch (Exception e) { + return ""; + } + } + + private ItemStack base64ToItem(String b64) { + try { + byte[] data = + Base64.getDecoder().decode(b64); + ByteArrayInputStream is = + new ByteArrayInputStream(data); + BukkitObjectInputStream bis = + new BukkitObjectInputStream(is); + ItemStack item = + (ItemStack) bis.readObject(); + bis.close(); + return item; + } catch (Exception e) { + return null; + } + } + + private void loadItemNbtData() { + itemNbtData.clear(); + itemStackCache.clear(); + File f = new File(plugin.getDataFolder(), + "shop/item_nbt.properties"); + if (!f.exists()) return; + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String id = + line.substring(0, eq).trim(); + String b64 = + line.substring(eq + 1).trim(); + itemNbtData.put(id, b64); + ItemStack stack = base64ToItem(b64); + if (stack != null) { + itemStackCache.put(id, stack); + } + } + br.close(); + // plugin.getLogger().info("[Shop] NBT数据: " + // + itemStackCache.size() + "条"); + } catch (Exception ignored) {} + } + + private void saveItemNbtData() { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, "item_nbt.properties"); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 商品完整数据(自动生成)"); + for (Map.Entry e + : itemNbtData.entrySet()) { + pw.println(e.getKey() + "=" + + e.getValue()); + } + pw.flush(); + pw.close(); + } catch (Exception ignored) {} + } + + // ===== 加入/移除购物车 ===== + public boolean addToCart(Player p, ShopItem item) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts + .computeIfAbsent( + uuid, k -> new ArrayList<>()); + // ★ 始终用原价,折扣在结算时统一应用 + int price = item.getBuyPrice(); + for (CartItem ci : cart) { + if (ci.itemId.equals(item.getId())) { + ci.quantity += 1; + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + } + CartItem ci = new CartItem(); + ci.itemId = item.getId(); + ci.displayName = item.getDisplayName(); + ci.material = item.getMaterial(); + ci.unitPrice = price; + ci.quantity = 1; + cart.add(ci); + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + + + public int getCartCount(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int c = 0; + for (CartItem ci : cart) c += ci.quantity; + return c; + } + + public int getCartTotal(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int t = 0; + for (CartItem ci : cart) + t += ci.unitPrice * ci.quantity; + return t; + } + + public void cartAddOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + cart.get(index).quantity += 1; + } + + public void cartRemoveOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + CartItem ci = cart.get(index); + ci.quantity -= 1; + if (ci.quantity <= 0) cart.remove(index); + } + + public void clearCart(Player p) { + shoppingCarts.remove(p.getUniqueId()); + } + + // ===== 打开购物车页面 ===== + public void openCart(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§a§l购物车"); + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + for (int i = 0; i < cart.size() && i < 25; i++) { + CartItem ci = cart.get(i); + ItemStack is = new ItemStack(ci.material); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + ci.displayName + + " §7x" + ci.quantity); + im.setLore(java.util.Arrays.asList( + "§7单价: §f" + ci.unitPrice + "枚", + "§7小计: §6" + + (ci.unitPrice * ci.quantity) + + "枚", + "", + "§a左键+1 §c右键-1")); + is.setItemMeta(im); + } + g.setItem(10 + i, is); + } + if (cart.isEmpty()) { + g.setItem(22, mkItem(Material.BARRIER, + "§7购物车为空")); + } + int total = getCartTotal(p); + int bal = plugin.getBonds().getBonds(p.getName()); + g.setItem(49, mkItem(Material.EMERALD, + "§6§l结算: §e" + total + " §6枚债券", + "§7余额: §f" + bal + "枚", + bal >= total ? "§a余额充足" : "§c余额不足", + "", "§a点击结算")); + g.setItem(48, mkItem( + Material.LIME_STAINED_GLASS_PANE, + "§7清空购物车")); + g.setItem(53, mkItem(Material.ARROW, "§7返回商店")); + + p.openInventory(g); + } + + // ===== 购物车点击处理 ===== + public boolean handleCartClick(Player p, int raw, + boolean left, + boolean right) { + if (raw == 53) { openShopMain(p); return true; } + if (raw == 48) { + clearCart(p); + p.sendMessage("§7购物车已清空"); + openCart(p); + return true; + } + if (raw == 49) { + checkoutCartInit(p); + return true; + } + if (raw >= 10 && raw < 35) { + int idx = raw - 10; + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + if (idx < cart.size()) { + if (right) cartRemoveOne(p, idx); + else cartAddOne(p, idx); + openCart(p); + } + return true; + } + return true; + } + + // ===== 结算入口:判断打包方式 ===== + private void checkoutCartInit(Player p) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts.get(uuid); + if (cart == null || cart.isEmpty()) { + p.sendMessage("§c购物车为空"); + return; + } + int total = getCartTotal(p); + int bal = plugin.getBonds() + .getBonds(p.getName()); + if (bal < total) { + p.sendMessage("§c余额不足,需要 §e" + + total + "§c枚"); + return; + } + int totalQty = 0; + for (CartItem ci : cart) totalQty += ci.quantity; + if (cart.size() == 1 && totalQty <= 64) { + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + deductAndGive(p, cart, + (int) Math.round(total * factor), + false, null, "", 0, + ecoPct, couponPct); + return; + } + pendingCheckout.put(uuid, + new ArrayList<>(cart)); + openPackingConfirm(p, total); + } + + + + // ===== 执行扣款+发货 ===== + private boolean deductAndGive(Player p, + List cart, int baseTotal, + boolean useShulker, Material color, + String packNote, int packFee, + int ecoPct, int couponPct) { + + UUID uuid = p.getUniqueId(); + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + String[] names = new String[cart.size()]; + int[] subs = new int[cart.size()]; + for (int i = 0; i < cart.size(); i++) { + CartItem ci = cart.get(i); + names[i] = ci.displayName; + subs[i] = ci.unitPrice * ci.quantity; + } + + if (baseTotal < originalTotal) { + double ratio = + (double) baseTotal / originalTotal; + int sum = 0; + for (int i = 0; i < subs.length; i++) { + if (i == subs.length - 1) { + subs[i] = baseTotal - sum; + } else { + subs[i] = (int) (subs[i] * ratio); + sum += subs[i]; + } + } + } + + boolean ok = plugin.getBonds().checkoutCart( + p.getName(), names, subs); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + if (packFee > 0) { + plugin.getBonds().deductBonds( + p.getName(), packFee, + "shop_packing", "", + "商店系统", "打包费"); + } + + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + if (si != null) { + if (si.getStock() >= 0) + si.setStock(si.getStock() + - ci.quantity); + si.addSales(ci.quantity); + } + } + + int newBal = plugin.getBonds() + .getBonds(p.getName()); + + + // ===== 优惠券码 ===== + String usedCouponCode = + pendingCouponCode.get(uuid); + + // ===== 记录订单 ===== + OrderManager.OrderRecord latestOrder = null; + try { + List orderItems = + new ArrayList<>(); + for (CartItem ci : cart) { + orderItems.add( + new OrderManager.OrderItem( + ci.displayName, + ci.material.name(), + ci.unitPrice, + ci.unitPrice, + ci.quantity)); + } + String dType = "none"; + String cCode = ""; + if (usedCouponCode != null) { + dType = "coupon"; + cCode = usedCouponCode; + } else if (ecoPct > 0) { + dType = "eco"; + } + plugin.getOrderManager().recordOrder( + p, orderItems, originalTotal, + baseTotal + packFee, + originalTotal - baseTotal, + dType, cCode, packFee, + useShulker ? (packFee > 0 + ? "custom" : "default") + : "none", + color != null ? color.name() : ""); + // 获取最新订单 + synchronized (plugin.getOrderManager() + .getOrders()) { + List all = + plugin.getOrderManager() + .getOrders(); + if (!all.isEmpty()) + latestOrder = + all.get(all.size() - 1); + } + // 散装打印小票 + if (!useShulker && latestOrder != null) { + plugin.getOrderManager() + .sendReceiptChat(p, latestOrder); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + // + ex.getMessage()); + } + +// ===== 发货 ===== + if (!useShulker) { + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + ItemStack give = (si != null) + ? getShopStack(si, ci.quantity) + : new ItemStack(ci.material, ci.quantity); + HashMap left2 = + p.getInventory().addItem(give); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } else { + List shulkers = + packShulkers(cart, color); + + // ★ 给潜影盒打订单ID标签 + if (latestOrder != null) { + for (ItemStack sh : shulkers) { + ItemMeta shm = sh.getItemMeta(); + if (shm != null) { + List lore = shm.getLore() != null + ? new ArrayList<>(shm.getLore()) + : new ArrayList<>(); + lore.add("§8#" + latestOrder.orderId); + shm.setLore(lore); + sh.setItemMeta(shm); + } + } + } + + // 小票书塞入 + if (!shulkers.isEmpty() + && latestOrder != null) { + ItemStack book = plugin.getOrderManager() + .createReceiptBook(latestOrder); + plugin.getOrderManager() + .addBookToShulker(shulkers.get(0), book); + } + // 发货 + for (ItemStack sh : shulkers) { + HashMap left2 = + p.getInventory().addItem(sh); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } + + + // ===== 退款记录 ===== + long now = System.currentTimeMillis(); + if (!useShulker) { + for (CartItem ci : cart) { + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + ci.itemId, ci.displayName, + ci.material, null, + ci.quantity, + ci.unitPrice * ci.quantity, + now)); + } + } else { + String summary = ""; + for (CartItem ci : cart) { + if (!summary.isEmpty()) summary += "+"; + summary += ci.displayName + + "x" + ci.quantity; + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + "cart_" + now, summary, + Material.SHULKER_BOX, color, + 1, baseTotal, now)); + } + + // ===== 核销优惠券 ===== + if (usedCouponCode != null) { + activeDiscount.remove(uuid); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCouponCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCouponCode + + " §7已核销"); + } + + shoppingCarts.remove(uuid); + pendingCheckout.remove(uuid); + return true; + } + + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + markShulkerUsed(item); + } + + // ★ 公共方法:标记潜影盒为已使用 + private void markShulkerUsed(ItemStack item) { + if (item == null) return; + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + // shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + @EventHandler + public void onPlayerDrop(org.bukkit.event.player.PlayerDropItemEvent e) { + ItemStack item = e.getItemDrop().getItemStack(); + if (item != null && item.getType().name().contains("SHULKER")) { + markShulkerUsed(item); + } + } + + + // ===== 潠影盒打包(指定颜色)===== + private List packShulkers( + List items, Material color) { + List result = new ArrayList<>(); + List all = new ArrayList<>(); + for (CartItem ci : items) { + int rem = ci.quantity; + while (rem > 0) { + int sz = Math.min(rem, 64); + all.add(new ItemStack(ci.material, sz)); + rem -= sz; + } + } + while (!all.isEmpty()) { + ItemStack shulker = new ItemStack(color); + org.bukkit.inventory.meta.BlockStateMeta meta = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (meta != null) { + org.bukkit.block.BlockState state = + meta.getBlockState(); + if (state instanceof org.bukkit.block.Container) { + org.bukkit.block.Container c = + (org.bukkit.block.Container) state; + List remain = new ArrayList<>(); + for (ItemStack stack : all) { + HashMap left = + c.getInventory().addItem(stack); + for (ItemStack l : left.values()) + remain.add(l); + } + all = remain; + meta.setBlockState(c); + shulker.setItemMeta(meta); + } + } + result.add(shulker); + } + + return result; + } + + // ===== 打包确认GUI ===== + public void openPackingConfirm(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l选择包装方式"); + g.setItem(10, mkItem(Material.CHEST, + "§a§l不打包", + "§7直接放入背包", + "§7适合少量物品")); + g.setItem(13, mkItem(Material.SHULKER_BOX, + "§6§l默认打包", + "§7随机颜色潜影盒", + "§7加收 §e5 §6枚债券")); + g.setItem(16, mkItem(Material.ENDER_CHEST, + "§b§l自选颜色", + "§7选择你喜欢的潜影盒颜色", + "§7加收 §e5 §6枚债券")); + g.setItem(22, mkItem(Material.PAPER, + "§7消费金额: §e" + baseTotal + "枚")); + + p.openInventory(g); + } + + public boolean handlePackingConfirm( + Player p, int raw) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + + if (raw == 10) { + ecoPct = 2; + } + + double factor = 1.0; + if (ecoPct > 0) + factor *= (100 - ecoPct) / 100.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + originalTotal * factor); + + int totalQty = 0; + for (CartItem ci : cart) + totalQty += ci.quantity; + boolean multiAndBig = (cart.size() > 1 + && originalTotal >= 100); + + if (raw == 10) { + deductAndGive(p, cart, discounted, + false, null, "", 0, + ecoPct, couponPct); + return true; + } + if (raw == 13) { + Material c = Material.SHULKER_BOX; + if (multiAndBig) { + deductAndGive(p, cart, discounted, + true, c, "", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, + true, c, "默认打包", 5, + ecoPct, couponPct); + } + return true; + } + if (raw == 16) { + pendingCheckout.put(uuid, cart); + openColorSelect(p, discounted); + return true; + } + return true; + } + + + // ===== 自选颜色GUI ===== + public void openColorSelect(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 54, "§b§l选择潜影盒颜色"); + String[] colorNames = { + "白色","橙色","品红","淡蓝", + "黄色","淡绿","粉色","灰色", + "淡灰","青色","紫色","蓝色", + "棕色","绿色","红色","黑色","原色" + }; + for (int i = 0; i < SHULKER_COLORS.length; i++) { + ItemStack is = new ItemStack(SHULKER_COLORS[i]); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + colorNames[i] + + "§6潜影盒"); + im.setLore(java.util.Arrays.asList( + "§7加收 §e5 §6枚债券", + "", + "§a点击选择")); + is.setItemMeta(im); + } + g.setItem(i, is); + } + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(45, mkItem(Material.PAPER, + "§7消费: §e" + baseTotal + + "枚 + §e5§6打包费")); + + p.openInventory(g); + } + + public boolean handleColorSelect( + Player p, int raw) { + if (raw == 49) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.get(uuid); + if (cart != null) { + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + openPackingConfirm(p, total); + } + return true; + } + if (raw < 0 || raw >= SHULKER_COLORS.length) + return false; + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + total * factor); + + Material selectedColor = SHULKER_COLORS[raw]; + if (selectedColor == Material.SHULKER_BOX) { + deductAndGive(p, cart, discounted, true, + Material.SHULKER_BOX, + "原色打包", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, true, + selectedColor, "自选颜色", 5, + ecoPct, couponPct); + } + return true; + } + + + + // ===== 加载分类 ===== + + public void loadCategories() { + // plugin.getLogger().info("[Shop] === loadCategories 被调用 ==="); // 过滤调试日志 + categories.clear(); + viewCat.clear(); // ★ 清缓存 + viewPage.clear(); + + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + + convertTxtToMd(dir); + + File[] allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + if (allMd == null || allMd.length == 0) { + createDefaultCategories(dir); + allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + } + if (allMd == null) { + // plugin.getLogger().warning( + // "[Shop] shop目录为空"); + return; + } + + /* plugin.getLogger().info("[Shop] === 开始加载 ==="); + plugin.getLogger().info("[Shop] 扫描到 " + + allMd.length + " 个md文件"); +*/ + for (File f : allMd) { + /* plugin.getLogger().info("[Shop] 读取: " + + f.getName());*/ + ShopCategory cat = parseFile(f); + if (cat != null) { + /* plugin.getLogger().info("[Shop] 分类: " + + cat.getName() + + " 商品数: " + + cat.getItems().size());*/ + if (!cat.getItems().isEmpty()) { + categories.add(cat); + } + } + } + + int totalItems = 0; + for (ShopCategory c : categories) + totalItems += c.getItems().size(); + + plugin.getLogger().info("[Shop] === 加载完成 ===" + + " 分类=" + categories.size() + + " 商品=" + totalItems); + } + + +// ===== txt → md 自动迁移 ===== + + private void convertTxtToMd(File dir) { + File[] txtFiles = dir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("coupons.txt")); + if (txtFiles == null) return; + for (File txt : txtFiles) { + String mdName = txt.getName() + .replace(".txt", ".md"); + File mdFile = new File(dir, mdName); + if (mdFile.exists()) continue; + + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(txt), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) + lines.add(line); + br.close(); + + String catName = txt.getName() + .replace(".txt", ""); + List out = new ArrayList<>(); + out.add("# " + catName); + out.add(""); + out.add("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + out.add("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + + for (String l : lines) { + l = l.trim(); + if (l.isEmpty() || l.startsWith("#")) + continue; + // 旧格式: ID | 名称 | 材质 | ... + // 保持原样转换为 md 表格行 + out.add("| " + l.replace("|", "|") + + " |"); + } + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(mdFile), + StandardCharsets.UTF_8)); + for (String o : out) + pw.println(o); + pw.flush(); + pw.close(); + + txt.delete(); + // plugin.getLogger().info( + // "[Shop] 已迁移: " + // + txt.getName() + // + " → " + mdName); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 迁移失败: " + // + txt.getName()); + } + } + } + +// ===== 解析 md 文件 ===== + + private ShopCategory parseFile(File f) { + String name = f.getName() + .replace(".md", ""); + ShopCategory cat = + new ShopCategory(name, f.getName()); + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean headerPassed = false; + boolean separatorPassed = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + + // 空行跳过 + if (line.isEmpty()) continue; + + // 标题行:# 分类名 + if (line.startsWith("#")) { + String t = line.replaceFirst( + "^#+\\s*", "").trim(); + if (!t.isEmpty()) { + // 更新分类名(用文件内标题) + // cat.name 是 final,不改 + } + continue; + } + + // 表格分隔行:| --- | --- | + if (line.contains("---")) { + separatorPassed = true; + continue; + } + + // 表头行:| ID | 品名 | ... + if (line.startsWith("|")) { + if (!separatorPassed) { + headerPassed = true; + continue; + } + // 数据行 + ShopItem item = + parseTableRow(line); + if (item != null) + cat.addItem(item); + } + } + br.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 解析失败: " + f.getName() + // + " 原因: " + e.getMessage()); + return null; + } + return cat; + } + +// ===== 解析表格行 ===== + + private ShopItem parseTableRow(String row) { + // plugin.getLogger().info("[Shop] 行: " + row); // 过滤调试日志 + + String trimmed = row.trim(); + if (trimmed.startsWith("|")) + trimmed = trimmed.substring(1); + if (trimmed.endsWith("|")) + trimmed = trimmed.substring(0, + trimmed.length() - 1); + + String[] p = trimmed.split("\\|"); + // plugin.getLogger().info("[Shop] 列数=" + p.length); // 过滤调试日志 + + if (p.length < 6) { + // plugin.getLogger().warning( + // "[Shop] ✗ 列数不足, 跳过"); + return null; + } + + String rawId = p[0].trim(); + String rawName = p[1].trim(); + String rawMat = p[2].trim().toUpperCase(); + String rawBuy = p[3].trim(); + String rawSell = p[4].trim(); + String rawStock = p[5].trim(); + + // plugin.getLogger().info("[Shop] ID=" + // + rawId + " 名=" + rawName + // + " 材质=" + rawMat + // + " 购=" + rawBuy + " 售=" + rawSell + // + " 库=" + rawStock); // 过滤调试日志 + + // 检查材质是否存在 + Material mat = Material.matchMaterial(rawMat); + if (mat == null) { + // plugin.getLogger().warning( + // "[Shop] ✗ 无效材质: " + rawMat + // + " (行: " + row + ")"); + return null; + } + + try { + ShopItem item = new ShopItem( + rawId, rawName, mat, + Integer.parseInt(rawBuy), + Integer.parseInt(rawSell), + Integer.parseInt(rawStock), + p.length > 6 + ? Integer.parseInt( + p[6].trim()) : 0, + p.length > 7 + ? Integer.parseInt( + p[7].trim()) : 0); + // plugin.getLogger().info("[Shop] ✓ 解析成功: " + rawId); // 过滤调试日志 + return item; + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] ✗ 数字解析失败: " + // + e.getMessage()); + return null; + } + } + + +// ===== 保存为 md ===== + + public void saveCategory(ShopCategory cat) { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, cat.getFileName()); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# " + cat.getName()); + pw.println(""); + pw.println("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + pw.println("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + for (ShopItem item : cat.getItems()) { + pw.println("| " + + item.getId() + + " | " + item.getDisplayName() + + " | " + item.getMaterial().name() + + " | " + item.getBuyPrice() + + " | " + item.getSellPrice() + + " | " + item.getStock() + + " | " + item.getHourlySales() + + " | " + item.getTotalSales() + + " |"); + } + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 保存失败: " + // + cat.getFileName()); + } + } + +// ===== 创建示例分类 ===== + + private void createDefaultCategories(File dir) { + writeMd(dir, "建筑材料.md", + "# 建筑材料\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| STONE | 石头 | STONE" + + " | 10 | -1 | -1 | 0 | 0 |\n" + + "| COBBLESTONE | 圆石 | COBBLESTONE" + + " | 5 | -1 | -1 | 0 | 0 |\n" + + "| OAK_PLANKS | 橡木木板 | OAK_PLANKS" + + " | 8 | 3 | -1 | 0 | 0 |\n" + + "| BRICKS | 砖块 | BRICKS" + + " | 12 | -1 | -1 | 0 | 0 |\n" + + "| GLASS | 玻璃 | GLASS" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| QUARTZ_BLOCK | 石英块 | QUARTZ_BLOCK" + + " | 20 | 8 | -1 | 0 | 0 |\n"); + + writeMd(dir, "装备工具.md", + "# 装备工具\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| IRON_PICKAXE | 铁镐 | IRON_PICKAXE" + + " | 50 | 20 | -1 | 0 | 0 |\n" + + "| DIAMOND_PICKAXE | 钻石镐 | DIAMOND_PICKAXE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| IRON_SWORD | 铁剑 | IRON_SWORD" + + " | 40 | 16 | -1 | 0 | 0 |\n" + + "| DIAMOND_SWORD | 钻石剑 | DIAMOND_SWORD" + + " | 400 | 160 | -1 | 0 | 0 |\n" + + "| ELYTRA | 鞘翅 | ELYTRA" + + " | 2000 | 800 | -1 | 0 | 0 |\n"); + + writeMd(dir, "食物补给.md", + "# 食物补给\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| COOKED_BEEF | 熟牛排 | COOKED_BEEF" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| GOLDEN_APPLE | 金苹果 | GOLDEN_APPLE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| BREAD | 面包 | BREAD" + + " | 8 | 3 | -1 | 0 | 0 |\n"); + + // plugin.getLogger().info( + // "[Shop] 已创建3个示例分类(md)"); + } + + private void writeMd(File dir, String name, + String content) { + File f = new File(dir, name); + if (f.exists()) return; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.print(content); + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 创建失败: " + name); + } + } + + /** + * 从商品名称+材质智能推断完整物品 + * + * "夜视药水" + POTION + * → PotionEffect(NIGHT_VISION, 600, 0) + * + * "喷溅型治疗药水" + SPLASH_POTION + * → PotionEffect(HEALING, 600, 0) + * + * "长效夜视之箭" + TIPPED_ARROW + * → PotionEffect(NIGHT_VISION, 1800, 0) + * + * "强效力量喷溅药水" + SPLASH_POTION + * → PotionEffect(STRENGTH, 600, 1) + */ + private ItemStack inferFromName( + String name, Material mat) { + // 只处理药水和箭 + if (mat != Material.POTION + && mat != Material.SPLASH_POTION + && mat != Material.LINGERING_POTION + && mat != Material.TIPPED_ARROW) { + return null; + } + + // 1. 找效果类型 + PotionEffectType effectType = null; + for (Map.Entry e + : EFFECT_MAP.entrySet()) { + if (name.contains(e.getKey())) { + effectType = e.getValue(); + break; + } + } + if (effectType == null) return null; + + // 2. 找时长 + int duration = 600; + if (name.contains("长效") || name.contains("长时") + || name.contains("持久") || name.contains("长命")) { + duration = 1800; + } +// ★ 瞬间效果药水:duration = 1 + if (name.contains("治疗") || name.contains("治愈") + || name.contains("伤害")) { + duration = 1; + } + + // 3. 找等级 + int amplifier = 0; + if (name.contains("强效") + || name.contains("强化") + || name.contains("加强")) { + amplifier = 1; + } + + // 4. 构造完整物品 + try { + ItemStack item = new ItemStack(mat); + PotionMeta meta = + (PotionMeta) item.getItemMeta(); + + // ★ 神龟药水:双效果 + if (name.contains("神龟")) { + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.RESISTANCE, + duration, amplifier + 2), + true); + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.SLOWNESS, + duration, amplifier + 3), + true); + } else { + // 普通药水:单效果 + meta.addCustomEffect( + new PotionEffect(effectType, + duration, amplifier), + true); + } + + item.setItemMeta(meta); + return item; + } catch (Exception e) { + return null; + } + } + + public void saveAll() { + for (ShopCategory cat : categories) + saveCategory(cat); + } + private ItemStack getShopStack( + ShopItem item, int amount) { + // 优先级1: 手动捕获的NBT数据 + ItemStack cached = + itemStackCache.get(item.getId()); + if (cached != null) { + ItemStack copy = cached.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级2: 根据名称智能推断 + ItemStack inferred = inferFromName( + item.getDisplayName(), + item.getMaterial()); + if (inferred != null) { + ItemStack copy = inferred.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级3: 纯材质(默认) + return new ItemStack( + item.getMaterial(), amount); + } + + // ===== 购买 ===== + + public boolean buyItem(Player p, ShopItem item, + int amount) { + // plugin.getLogger().info("[Shop] ★ buyItem item=" + (item != null ? item.getId() : "null") + " amount=" + amount); + // plugin.getLogger().info("buyItem被调用"); + if (item == null) return false; + if (item.getStock() >= 0 + && item.getStock() < amount) { + p.sendMessage("§c库存不足,剩余: " + + item.getStock()); + return false; + } + if (p.getInventory().firstEmpty() == -1 + && !p.getInventory().contains( + item.getMaterial(), 1)) { + p.sendMessage("§c背包已满"); + return false; + } + + int unit = item.getBuyPrice(); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int total = Math.max(0, + unit * amount * (100 - disc) / 100); + + int balance = plugin.getBonds() + .getBonds(p.getName()); + if (balance < total) { + p.sendMessage("§c债券不足,需要 §e" + + total + "§c枚,当前 §e" + + balance + "§c枚"); + return false; + } + + boolean ok = plugin.getBonds().deductBonds( + p.getName(), total, + "shop_buy", item.getId(), + "商店系统", "购买" + + item.getDisplayName()); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + ItemStack give = getShopStack(item, amount); + HashMap left = + p.getInventory().addItem(give); + for (ItemStack drop : left.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + + if (item.getStock() >= 0) + item.setStock( + item.getStock() - amount); + item.addSales(amount); + + mergeOrAddRefund(p, item, amount, total); + Long applyTime = couponApplyTime.get(p.getUniqueId()); + if (applyTime != null + && System.currentTimeMillis() - applyTime > 300000L) { + // 超过5分钟,自动失效 + pendingCouponCode.remove(p.getUniqueId()); + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + p.sendMessage("§7优惠券已过期,请重新输入"); + disc = 0; // 重置折扣 + } + + + String discMsg = disc > 0 + ? " §7(优惠" + disc + "%)" : ""; + p.sendMessage("§a购买成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a-" + total + "枚债券" + + discMsg); + if (disc > 0) + activeDiscount.remove( + p.getUniqueId()); + // ★ 直购成功后核销优惠券 + String usedCode = pendingCouponCode + .remove(p.getUniqueId()); + if (usedCode != null) { + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCode + + " §7已核销"); + couponApplyTime.remove(p.getUniqueId()); + } + // ★ 记录订单(包裹try-catch,失败不影响购买) + try { + String usedCouponCode = + pendingCouponCode.get(p.getUniqueId()); + List orderItems = + new ArrayList<>(); + orderItems.add(new OrderManager.OrderItem( + item.getDisplayName(), + item.getMaterial().name(), + item.getBuyPrice(), + item.getBuyPrice(), amount)); + plugin.getOrderManager().recordOrder( + p, orderItems, + item.getBuyPrice() * amount, total, + item.getBuyPrice() * amount - total, + usedCouponCode != null ? "coupon" : "none", + usedCouponCode != null ? usedCouponCode : "", + 0, "none", ""); + // 打印小票 + List all = + plugin.getOrderManager().getOrders(); + if (!all.isEmpty()) { + plugin.getOrderManager().sendReceiptChat( + p, all.get(all.size() - 1)); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + ex.getMessage()); + } + + return true; + } + + + public void handleRefundItemClick(Player p, int raw) { + if (raw < 10 || raw >= 45) return; + int target = raw - 10; + int cnt = 0; + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { it.remove(); continue; } + if (cnt == target) { + requestRefund(p, r); + return; + } + cnt++; + } + } + } + + + // ===== 出售 ===== + + public boolean sellItem(Player p, ShopItem item, + int amount) { + if (item == null) return false; + + // ★ 先判断售价,禁止出售的不收物品 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return false; + } + + if (!p.getInventory().containsAtLeast( + getShopStack(item, 1), amount)) { + p.sendMessage("§c你没有足够的 §e" + + item.getDisplayName()); + return false; + } + + int total = item.getEffectiveSellPrice() * amount; + + p.getInventory().removeItem( + getShopStack(item, amount)); + plugin.getBonds().addBonds( + p.getName(), total, "shop_sell", + item.getId(), "商店系统", + "出售" + item.getDisplayName()); + if (item.getStock() >= 0) + item.setStock(item.getStock() + amount); + p.sendMessage("§a出售成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a+" + total + "枚债券"); + return true; + } + + // ===== 退款合并:30秒内同商品合并为一个订单 ===== + private boolean mergeOrAddRefund(Player p, ShopItem item, + int amount, int total) { + long now = System.currentTimeMillis(); + synchronized (refundRecords) { + for (int i = refundRecords.size() - 1; i >= 0; i--) { + RefundRecord r = refundRecords.get(i); + boolean same = r.playerUUID.equals(p.getUniqueId()) + && r.itemId.equals(item.getId()) + && (now - r.timestamp) < 30000L; + if (same) { + // final 字段不能改,只能删旧建新 + int newAmount = r.amount + amount; + int newTotal = r.totalPaid + total; + refundRecords.remove(i); + refundRecords.add(new RefundRecord( + r.playerUUID, r.playerName, + r.itemId, r.itemName, + r.material, newAmount, + newTotal, now)); + return true; + } + } + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + item.getId(), item.getDisplayName(), + item.getMaterial(), amount, total, now)); + return false; + } + + + + // ===== 退款 ===== + + public boolean requestRefund(Player p, RefundRecord rec) { + if (rec == null) return false; + if (!rec.playerUUID.equals(p.getUniqueId())) { + p.sendMessage("§c这不是你的购买记录"); + return false; + } + if (rec.isExpired()) { + p.sendMessage("§c已超过5分钟退款期限"); + synchronized (refundRecords) { + refundRecords.remove(rec); + } + return false; + } + + // 检测物品/潜影盒是否还在 + synchronized (refundRecords) { + if (rec.shulkerMaterial != null) { + int boxCount = countShulkers(p, rec.shulkerMaterial); + if (boxCount < 1) { + p.sendMessage("§c潜影盒已移出背包,无法退款"); + refundRecords.remove(rec); + return false; + } + } else { + int have = countDirect(p, rec.material); + if (have < rec.amount) { + p.sendMessage("§c物品已转移或消耗,无法退款"); + refundRecords.remove(rec); + return false; + } + } + } + + // 退款:扣物品/潜影盒 + if (rec.shulkerMaterial != null) { + // 盒装:扣一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == rec.shulkerMaterial) { + p.getInventory().clear(i); + break; + } + } + } else { + // 普通:扣商品 + p.getInventory().removeItem( + new ItemStack(rec.material, rec.amount)); + } + + // 退款:加债券 + plugin.getBonds().addBonds( + p.getName(), rec.totalPaid, + "refund", rec.itemId, + "商店系统", "退款" + rec.itemName); + + // 恢复库存 + ShopItem si = findItemById(rec.itemId); + if (si != null && si.getStock() >= 0) + si.setStock(si.getStock() + rec.amount); + + synchronized (refundRecords) { + refundRecords.remove(rec); + } + + p.sendMessage("§a退款成功: §e" + rec.itemName + + " §a+" + rec.totalPaid + "枚债券"); + return true; + } + + + + + public void setPlayerDiscount(Player p, + int pct) { + activeDiscount.put( + p.getUniqueId(), pct); + p.sendMessage("§a已设置 " + pct + + "% 折扣,下次购买生效"); + } + + // ===== 管理员操作 ===== + + public boolean addItemToCategory( + String catName, String id, + String displayName, Material mat, + int buyPrice, int sellPrice, + int stock) { + ShopCategory cat = + findCategory(catName); + if (cat == null) return false; + if (cat.getItem(id) != null) return false; + cat.addItem(new ShopItem(id, displayName, + mat, buyPrice, sellPrice, + stock, 0, 0)); + saveCategory(cat); + return true; + } + + public boolean removeItem(String id) { + for (ShopCategory cat : categories) { + ShopItem item = cat.getItem(id); + if (item != null) { + cat.getItems().remove(item); + saveCategory(cat); + return true; + } + } + return false; + } + + public boolean setStock(String id, int stock) { + ShopItem item = findItemById(id); + if (item == null) return false; + item.setStock(stock); + for (ShopCategory cat : categories) { + if (cat.getItem(id) != null) { + saveCategory(cat); + return true; + } + } + return false; + } + + public ShopCategory findCategory(String n) { + for (ShopCategory c : categories) + if (c.getName().equals(n)) return c; + return null; + } + + public ShopItem findItemById(String id) { + for (ShopCategory c : categories) { + ShopItem item = c.getItem(id); + if (item != null) return item; + } + return null; + } + + public List getCategories() { + return categories; + } + + // ===== 命令处理 ===== + + public boolean handleCommand( + CommandSender s, String[] a) { + if (a.length <= 1) { + if (s instanceof Player) { + openShopMain((Player) s); + } else { + s.sendMessage("§c仅玩家可使用"); + } + return true; + } + + String sub = a[1].toLowerCase(); + + if (sub.equals("reload")) { + loadCategories(); + s.sendMessage("§a商店已重载,共 " + + categories.size() + + " 个分类"); + return true; + } + + if (sub.equals("add")) { + if (a.length < 7) { + s.sendMessage( + "§e用法: shop add <分类>" + + " <名称> <材质>" + + " <购入价>" + + " [售出价-1自动]" + + " [库存-1无限]"); + return true; + } + Material mat; + try { + mat = Material.valueOf( + a[5].toUpperCase()); + } catch (Exception e) { + s.sendMessage( + "§c无效材质: " + a[5]); + return true; + } + int price = Integer.parseInt(a[6]); + int sell = a.length > 7 + ? Integer.parseInt(a[7]) : -1; + int stock = a.length > 8 + ? Integer.parseInt(a[8]) : -1; + if (addItemToCategory(a[2], a[3], + a[4], mat, price, sell, stock)) { + s.sendMessage("§a已添加: " + + a[4] + " → " + a[2]); + } else { + s.sendMessage("§c添加失败" + + "(分类不存在或ID重复)"); + } + return true; + } + + if (sub.equals("remove")) { + if (a.length < 3) { + s.sendMessage( + "§e用法: shop remove "); + return true; + } + if (removeItem(a[2])) { + s.sendMessage( + "§a已移除: " + a[2]); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("set")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop set " + + " <库存数(-1无限)>"); + return true; + } + int stock = Integer.parseInt(a[3]); + if (setStock(a[2], stock)) { + s.sendMessage("§a已设置 " + + a[2] + " 库存为 " + + stock); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("pro")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop pro" + + " <玩家名> <折扣%>"); + return true; + } + Player t = + Bukkit.getPlayerExact(a[2]); + if (t == null) { + s.sendMessage("§c玩家不在线"); + return true; + } + int pct = Integer.parseInt(a[3]); + setPlayerDiscount(t, pct); + s.sendMessage("§a已对 " + a[2] + + " 设置 " + pct + "% 折扣"); + return true; + } + + + s.sendMessage("§e商店子命令:" + + " reload / add / remove" + + " / set / pro "); + return true; + } + @EventHandler(priority = EventPriority.LOW) + public void onCouponChat( + org.bukkit.event.player.AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + if (!couponListening.contains(uuid)) return; + e.setCancelled(true); + couponListening.remove(uuid); + String code = e.getMessage().trim(); + if (code.isEmpty()) { + p.sendMessage("§c已取消输入"); + return; + } + // ★ 预处理:标准化输入 + String normalized = normalizeCouponInput(code); + if (normalized.isEmpty()) { + p.sendMessage("§c优惠券码无效(输入为空或仅含特殊字符)"); + couponListening.remove(uuid); + return; + } + + p.sendMessage("§7正在验证优惠券: §e" + code); + + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "validateCoupon", + String.class); + Object result = m.invoke(sdf1, normalized); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception ex) { + p.sendMessage("§c验证异常: " + + ex.getMessage()); + return; + } + if (discount <= 0) { + p.sendMessage("§c优惠券无效或已使用"); + return; + } + int offPct = 100 - discount; + int current = activeDiscount + .getOrDefault(uuid, 0); + if (offPct > current) { + pendingCouponCode.put(uuid, normalized); + activeDiscount.put(uuid, offPct); + couponApplyTime.put(uuid, System.currentTimeMillis()); + String zhe; + if (discount % 10 == 0) + zhe = (discount / 10) + "折"; + else + zhe = (discount / 10.0) + "折"; + p.sendMessage("§6§l[优惠券] §a验证通过!"); + p.sendMessage("§7折扣: §e" + zhe + + " §7(减" + offPct + "%)"); + p.sendMessage("§7结算时自动核销"); + final UUID u = uuid; + Bukkit.getScheduler().runTask(plugin, () -> { + Player pl = Bukkit.getPlayer(u); + if (pl != null && pl.isOnline()) { + openShopMain(pl); + } + }); + } else { + String curZhe; + if ((100 - current) % 10 == 0) + curZhe = ((100 - current) / 10) + "折"; + else + curZhe = ((100 - current) / 10.0) + "折"; + p.sendMessage("§c当前已有更优折扣: " + + curZhe + ",本次不生效"); + } + } + + /** + * 优惠券输入标准化 + * 全角→半角、去分隔符、转小写 + * "AAA-BBB-CCCC" → "aaaabbbbcccc" + * "AAA-BBB" → "aaabbb" + * "aaa_bbb_cccc" → "aaabbbcccc" + */ + private String normalizeCouponInput(String s) { + if (s == null) return ""; + StringBuilder sb = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + // 全角数字→半角 + if (c >= '\uFF10' && c <= '\uFF19') { + sb.append((char)(c - 0xFF10 + '0')); + } + // 全角字母→半角 + else if (c >= '\uFF21' && c <= '\uFF3A') { + sb.append((char)(c - 0xFF21 + 'A')); + } + else if (c >= '\uFF41' && c <= '\uFF5A') { + sb.append((char)(c - 0xFF41 + 'a')); + } + // ★ 保留中文字符(Unicode CJK统一汉字范围) + else if ((c >= '\u4E00' && c <= '\u9FFF') + || (c >= '\u3400' && c <= '\u4DBF')) { + sb.append(c); + } + // 保留半角字母和数字 + else if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9')) { + sb.append(c); + } + // 分隔符(- _ 空格等)丢弃 + } + return sb.toString().toLowerCase(); + } + + + + + + private String formatZhe(int discount) { + if (discount % 10 == 0) { + return (discount / 10) + "折"; + } + return (discount / 10.0) + "折"; + } + + public boolean redeemCoupon(Player p, String code) { + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + Object result = m.invoke(sdf1, code); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception e) { + return false; + } + if (discount <= 0) return false; + int offPct = 100 - discount; + int cur = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (offPct > cur) { + activeDiscount.put(p.getUniqueId(), offPct); + } + return true; + } + + /** + * 商店主界面点击"优惠券"按钮后调用 + * 开启聊天监听,等待玩家输入优惠券码 + */ + public void openCouponInput(Player p) { + UUID uuid = p.getUniqueId(); + // 先取消已有的待核销优惠券 + pendingCouponCode.remove(uuid); + couponListening.add(uuid); + // 关闭UI + Bukkit.getScheduler().runTask(plugin, () -> { + if (p.isOnline()) p.closeInventory(); + }); + p.sendMessage("§6§l[优惠券] §e请在聊天栏输入优惠券码"); + p.sendMessage("§715秒内输入,过期自动取消"); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (couponListening.remove(uuid)) { + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§c优惠券输入已超时取消"); + } + } + }, 300L); + } + + + // ===== GUI: 主界面 ===== + + public void openShopMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l商店"); + int si = 0; + for (ShopCategory cat : categories) { + if (si >= ITEM_SLOTS.length) break; + ItemStack is = new ItemStack( + Material.CHEST); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName( + "§e§l" + cat.getName()); + im.setLore(Arrays.asList( + "§7商品: §f" + + cat.getItems().size() + + " 种", + "", + "§a点击进入")); + is.setItemMeta(im); + } + g.setItem(ITEM_SLOTS[si], is); + si++; + } + // ★ 改为跳转订单中心 + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看订单记录、申请退款", + "", "§a点击进入")); + + // 在 openShopMain 方法中,原来的 slot 49/53 附近加: + + g.setItem(48, mkItem( + isCartMode(p) + ? Material.LIME_WOOL + : Material.GRAY_WOOL, + isCartMode(p) + ? "§a§l购物车模式 §7[开]" + : "§7§l购物车模式 §7[关]", + isCartMode(p) + ? "§a当前: 点击商品加入购物车" + : "§7当前: 直接购买", + "§7点击切换")); + g.setItem(50, mkItem(Material.CHEST, + "§6§l购物车 §7[" + getCartCount(p) + "件]", + "§7总价: §6" + getCartTotal(p) + "枚", + "", + "§a点击打开购物车")); + + // 在现有按钮之后,p.openInventory(g) 之前加: + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看购物订单记录", + "", "§a点击进入")); + + // 优惠券按钮 - slot 47 + String discStatus = "§7"; + int curDisc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (curDisc > 0) { + discStatus = "§a当前: " + formatZhe(100 - curDisc); + } + g.setItem(47, mkItem(Material.NAME_TAG, + "§6§l优惠券", + discStatus, + "", + "§a点击输入优惠券码", + "§7一次性使用,核销后失效")); + + g.setItem(53, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + // ===== GUI: 分类 ===== + public void openCategory(Player p, + ShopCategory cat, + int page) { + // ★ 每次重新获取最新对象 ★ + ShopCategory fresh = null; + for (ShopCategory c : categories) { + if (c.getName().equals(cat.getName())) { + fresh = c; + break; + } + } + if (fresh == null) { + openShopMain(p); + return; + } + cat = fresh; + viewCat.put(p.getUniqueId(), cat); + + int total = (int) Math.ceil( + (double) cat.getItems().size() + / PAGE_SIZE); + if (total < 1) total = 1; + if (page < 0) page = 0; + if (page >= total) page = total - 1; + viewPage.put(p.getUniqueId(), page); + + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l" + cat.getName()); + + + int start = page * PAGE_SIZE; + int end = Math.min(start + PAGE_SIZE, + cat.getItems().size()); + + for (int i = start; i < end; i++) { + int idx = i - start; + if (idx >= ITEM_SLOTS.length) break; + + // ★ item 保留原名,代表 ShopItem 数据 + ShopItem item = cat.getItems().get(i); + + // ★ is 是带完整元数据的 ItemStack(用于GUI图标) + ItemStack is = getShopStack(item, 1); + ItemMeta im = is.getItemMeta(); + + if (im != null) { + im.setDisplayName("§e§l" + + item.getDisplayName()); + List lore = new ArrayList<>(); + lore.add("§7购入: §a" + + item.getBuyPrice() + + "§7枚债券"); + lore.add("§7售出: §e" + + item.getEffectiveSellPrice() + + "§7枚债券"); + if (item.getStock() >= 0) + lore.add("§7库存: §f" + + item.getStock()); + else + lore.add("§7库存: §a无限"); + lore.add(""); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (disc > 0) { + int dis = item.getBuyPrice() + * (100 - disc) / 100; + lore.add("§7优惠价: §6" + + dis + "枚" + + " §7(-" + disc + "%)"); + } + lore.add("§a左键买1 §6右键卖1"); + lore.add("§aShift+左键买64" + + " §6Shift+右键卖64"); + im.setLore(lore); + is.setItemMeta(im); + } + + // ★ 用 is(ItemStack)放入GUI格子 + g.setItem(ITEM_SLOTS[idx], is); + } + + + g.setItem(4, mkItem(Material.BOOK, + "§e§l" + cat.getName(), + "§7共 " + cat.getItems().size() + + " 种商品")); + g.setItem(48, page > 0 + ? mkItem(Material.ARROW, + "§7上一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(49, mkItem(Material.PAPER, + "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 + ? mkItem(Material.ARROW, + "§7下一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, + "§7返回商店")); + + p.openInventory(g); + } + + // ===== GUI: 退款 ===== + + public void openRefundPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§c§l退款中心"); + + // 位移检测:比对上次记录的背包内容 + validateRefunds(p); + + // 过期清理 + synchronized (refundRecords) { + refundRecords.removeIf(RefundRecord::isExpired); + } + + int idx = 0; + synchronized (refundRecords) { + for (RefundRecord r : refundRecords) { + if (idx >= 45) break; + if (!r.playerUUID.equals(p.getUniqueId())) continue; + if (r.isExpired()) continue; + + // 盒装用潜影盒图标,普通用商品图标 + Material iconMat = (r.shulkerMaterial != null) + ? r.shulkerMaterial : r.material; + + ItemStack is = new ItemStack(iconMat); + ItemMeta im = is.getItemMeta(); + if (im != null) { + int secs = r.getSecondsLeft(); + im.setDisplayName("§e" + r.itemName + + (r.shulkerMaterial != null + ? " §7[盒装]" : " x" + r.amount)); + + List lore = new ArrayList<>(); + lore.add("§7订单: §f#" + r.timestamp); + lore.add("§7支付: §a" + r.totalPaid + + "§7枚债券"); + + if (r.shulkerMaterial != null) { + int boxCount = countShulkers(p, + r.shulkerMaterial); + lore.add("§7潜影盒: §f" + + (boxCount >= 1 + ? "§a在背包中" + : "§c已移出")); + } else { + int have = countDirect(p, r.material); + lore.add("§7物品: §f" + have + + "/" + r.amount); + } + + lore.add("§7剩余: §e" + secs + "秒"); + lore.add(""); + lore.add("§c点击退款"); + im.setLore(lore); + } + is.setItemMeta(im); + g.setItem(10 + idx, is); + idx++; + } + } + + if (idx == 0) { + g.setItem(22, mkItem(Material.BARRIER, + "§7暂无可退款记录")); + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + + p.openInventory(g); + } + // 统计背包中某物品数量 + private int countInInventory(Player p, Material mat) { + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == mat) { + count += is.getAmount(); + } + } + // 潜影盒也要检查里面 + for (ItemStack is : p.getInventory().getContents()) { + if (is != null + && is.getType().name() + .endsWith("_SHULKER_BOX")) { + count += is.getAmount(); + } + } + return count; + } + + // 只统计背包中直接可见的数量(不含盒内) + // 新代码:增加按完整物品匹配的方法 + private int countDirect(Player p, Material mat) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.getType() == mat) + count += is.getAmount(); + } + return count; + } + + private int countDirectByStack(Player p, + ItemStack target) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.isSimilar(target)) + count += is.getAmount(); + } + return count; + } + // 统计背包中潜影盒数量 + private int countShulkers(Player p, Material shulker) { + if (shulker == null) return Integer.MAX_VALUE; + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == shulker) + count += is.getAmount(); + } + return count; + } + + // 退款检测:位移 OR 消耗 → 不能退 + private void validateRefunds(Player p) { + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { + it.remove(); + continue; + } + + if (r.shulkerMaterial != null) { + // 盒装:检测潜影盒是否还在背包 + int now = countShulkers( + p, r.shulkerMaterial); + if (now < 1) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 潜影盒已移出背包,无法退款"); + } + } else { + // 普通:检测商品是否还在背包 + int now = countDirect(p, r.material); + if (now < r.amount) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 物品已被使用或移出,无法退款"); + } + } + } + } + } + + + public int getPlayerPage(java.util.UUID uuid) { + return viewPage.getOrDefault(uuid, 0); + } + + public ShopCategory getPlayerCategory(java.util.UUID uuid) { + return viewCat.get(uuid); + } + + // ===== 供GUIManager调用:商店主界面点击 ===== + public boolean handleMainShopClick(Player p, int raw) { + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true; } + + if (raw == 48) { + toggleCartMode(p); + openShopMain(p); + return true; + } + if (raw == 50) { + openCart(p); + return true; + } + if (raw == 53) { + plugin.getGui().openMain(p); + return true; + } + + // ★ 新增:优惠券按钮 + if (raw == 47) { + openCouponInput(p); + return true; + } + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true;} + int idx = slotToIndex(raw); + if (idx < 0 || idx >= categories.size()) + return true; + openCategory(p, categories.get(idx), 0); + return true; + } + + // ===== 供GUIManager调用:退款中心点击 ===== + public boolean handleRefundClick(Player p, int raw) { + if (raw == 49) { openShopMain(p); return true; } + return true; + } + + // ===== 供GUIManager调用:分类页点击 ===== + public boolean handleCategoryClick(Player p, String title, + int raw, boolean left, + boolean shift) { + // plugin.getLogger().info("[Shop] ★ handleCategoryClick raw=" + raw); + + ShopManager sm = this; + ShopCategory match = null; + for (ShopCategory c : categories) { + if (title.equals("§6§l" + c.getName())) { + match = c; + break; + } + } + if (match == null) { + // plugin.getLogger().info("[Shop] ★ 匹配失败, title=[" + title + "]"); + return false; + } + // plugin.getLogger().info("[Shop] ★ 匹配到: " + match.getName()); + + viewCat.put(p.getUniqueId(), match); + + if (raw == 53) { openShopMain(p); return true; } + int pg = getPlayerPage(p.getUniqueId()); + if (raw == 48) { openCategory(p, match, pg - 1); return true; } + if (raw == 50) { openCategory(p, match, pg + 1); return true; } + + int itemIdx = slotToIndex(raw); + if (itemIdx < 0) { + // plugin.getLogger().info("[Shop] ★ slot=" + raw + " 不是商品格"); + return true; + } + int actual = itemIdx + pg * PAGE_SIZE; + if (actual < 0 || actual >= match.getItems().size()) { + // plugin.getLogger().info("[Shop] ★ 超出范围 actual=" + actual + " size=" + match.getItems().size()); + return true; + } + ShopItem si = match.getItems().get(actual); + // plugin.getLogger().info("[Shop] ★ 准备购买: " + si.getId() + " left=" + left); + + int amount = shift ? 64 : 1; + if (left) { + if (shift && isCartMode(p)) { addToCart(p, si); } + else if (shift) { buyItem(p, si, 64); } + else if (isCartMode(p)) { addToCart(p, si); } + else { buyItem(p, si, 1); } + } else { + if (shift) { sellAllOf(p, si); } + else { sellItem(p, si, 1); } + } + openCategory(p, match, pg); + return true; + } + + // ===== 批量出售某商品全部数量 ===== + private void sellAllOf(Player p, ShopItem item) { + if (item == null) return; + // ★ 补上禁止出售检查 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return; + } + if (item == null) return; + Material mat = item.getMaterial(); + int total = 0; + int count = 0; + // 遍历背包清点 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + total += is.getAmount(); + count++; + } + } + if (total <= 0) { + p.sendMessage("§c你没有可出售的 §e" + + item.getDisplayName()); + return; + } + // 清除背包中该物品 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + } + } + int money = item.getEffectiveSellPrice() * total; + plugin.getBonds().addBonds( + p.getName(), money, "shop_sell", + item.getId(), "商店系统", + "批量出售" + item.getDisplayName() + + " x" + total); + if (item.getStock() >= 0) + item.setStock(item.getStock() + total); + p.sendMessage("§a批量出售: §e" + + item.getDisplayName() + + " x" + total + + " §a+" + money + "枚债券"); + } + + + // ===== 定时 ===== + + private void startHourlyTask() { + new BukkitRunnable() { + @Override + public void run() { + // ★ 汇报交给 SalesStatsManager + if (plugin.getSalesStats() != null) { + plugin.getSalesStats().report(); + } + // 重置小时销量 + for (ShopCategory cat : categories) + for (ShopItem item : cat.getItems()) + item.setHourlySales(0); + saveAll(); + } + }.runTaskTimer(plugin, 72000L, 72000L); + } + + + private void startCleanupTask() { + new BukkitRunnable() { + @Override + public void run() { + synchronized (refundRecords) { + refundRecords.removeIf( + RefundRecord::isExpired); + } + } + }.runTaskTimer(plugin, 1200L, 1200L); + } + + // ===== 工具 ===== + + private int slotToIndex(int slot) { + for (int i = 0; i < ITEM_SLOTS.length; i++) + if (ITEM_SLOTS[i] == slot) return i; + return -1; + } + + private ItemStack mkItem(Material mat, + String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/TicketManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/TicketManager.java new file mode 100644 index 0000000..c21edc2 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/TicketManager.java @@ -0,0 +1,1644 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class TicketManager { + + private final Main plugin; + + public static final String T_TICKET_MAIN = + "§6§l工单中心"; + public static final String T_TICKET_CREATE = + "§e§l创建工单"; + public static final String T_TICKET_MY = + "§b§l我的工单"; + public static final String T_TICKET_ADMIN = + "§c§l工单管理"; + public static final String T_TICKET_DETAIL = + "§a§l工单详情"; + + public static final String TYPE_BUG = + "bug反馈"; + public static final String TYPE_HELP = + "求助"; + public static final String TYPE_REPORT = + "举报"; + public static final String TYPE_APPLY = + "申请"; + public static final String TYPE_OTHER = + "其他"; + public static final String TYPE_DELETE = + "删号"; + public static final String TYPE_PASSWORD = + "修改密码"; + + + + private static final Map + PRICES = new LinkedHashMap<>(); + static { + PRICES.put(TYPE_BUG, 10); + PRICES.put(TYPE_HELP, 5); + PRICES.put(TYPE_REPORT, 8); + PRICES.put(TYPE_APPLY, 15); + PRICES.put(TYPE_OTHER, 5); + PRICES.put(TYPE_DELETE, 0); + PRICES.put(TYPE_PASSWORD, 0); + } + + + public TicketManager(Main plugin) { + this.plugin = plugin; + } + + // ========== GUI ========== + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_MAIN); + fillBg(g); + g.setItem(10, mk(Material.PAPER, + "§e创建工单", + "§7提交bug、求助、举报等")); + g.setItem(12, mk(Material.BOOK, + "§b我的工单", + "§7查看已提交的工单")); + g.setItem(14, mk(Material.CHEST, + "§6工单记录", + "§7查看历史工单")); + if (adm(p)) { + g.setItem(16, mk(Material.REDSTONE, + "§c工单管理", + "§7管理员专用")); + } + if (plugin.getDb() + .isServiceProvider( + p.getName())) { + g.setItem(18, mk(Material.EMERALD, + "§a服务商面板", + "§7接单、处理工单")); + } + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openCreate(Player p) { + Inventory g = Bukkit.createInventory( + null, 36, T_TICKET_CREATE); + fillBg(g); + g.setItem(10, mk(Material.BOOK, + "§cbug反馈")); + g.setItem(11, mk(Material.EMERALD, + "§a求助")); + g.setItem(12, mk(Material.BLAZE_POWDER, + "§6举报")); + g.setItem(13, mk(Material.NAME_TAG, + "§e申请")); + g.setItem(14, mk(Material.PAPER, + "§7其他")); + g.setItem(20, mk(Material.BARRIER, + "§c申请删号", + "§7申请删除账号")); + g.setItem(21, mk(Material.NETHER_WART, + "§d申请改密", + "§7请求管理员协助改密")); + g.setItem(31, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyTickets(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_MY); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByPlayer( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openDetail(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_DETAIL); + fillBg(g); + + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = sv(t, "requester"); + String title = sv(t, "title"); + String desc = sv(t, "description"); + String assigned = sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + int confirmed = nv(t, "admin_confirmed"); + + g.setItem(10, mk(Material.PAPER, + "§e§l" + (title.isEmpty() + ? type : title), + "§7类型: §f" + type, + "§7状态: " + stxt(status), + "§7提交者: §f" + requester, + "§7奖励: §e" + reward + " 积分")); + + g.setItem(12, mk(Material.BOOK, + "§7详细描述", + desc.isEmpty() ? "§7无描述" + : "§7" + (desc.length() > 40 + ? desc.substring(0, 40) + + "..." : desc))); + + if (!assigned.isEmpty()) { + g.setItem(14, mk(Material.PLAYER_HEAD, + "§7处理人: §f" + assigned, + confirmed == 1 + ? "§7评分: §e" + score + : "§7处理中")); + } else { + g.setItem(14, mk(Material.BARRIER, + "§7未分配")); + } + + // 驳回原因 + if ("closed".equals(status)) { + String reason = sv(t, "reject_reason"); + if (!reason.isEmpty()) { + g.setItem(13, mk(Material.BARRIER, + "§c驳回原因", + "§7" + reason)); + } + } + + // 回复按钮(非关闭/已完结状态) + if (!"closed".equals(status) + && !"withdrawn".equals(status) + && !"resolved".equals(status)) { + g.setItem(15, mk( + Material.WRITABLE_BOOK, + "§b回复工单", + "§7向对方发送消息")); + g.setItem(16, mk(Material.BOOK, + "§7查看回复记录")); + } + + // ===== 管理员按钮 ===== + if (adm(p)) { + if ("pending".equals(status)) { + // 待分配:可分配给服务商 + g.setItem(19, mk(Material.EMERALD, + "§a分配工单", + "§7指定服务商处理")); + // 也可自己处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && !assigned.equals("admin") + && !assigned.isEmpty()) { + // 服务商在处理:可取消指派收回 + g.setItem(19, mk(Material.BARRIER, + "§c取消指派", + "§7取消服务商指派,收回工单")); + // 也可自己接手处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && assigned.equals("admin")) { + // 管理员自己在处理:可标记完结 + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + if ("replied".equals(status) + && assigned.equals("admin")) { + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + // 驳回(非已关闭/已完结状态) + if (!"closed".equals(status) + && !"resolved".equals(status) + && !"withdrawn".equals(status)) { + g.setItem(21, mk(Material.RED_DYE, + "§c驳回工单", + "§7驳回并关闭此工单")); + } + } + + // ===== 服务商按钮 ===== + if (plugin.getDb() + .isServiceProvider(p.getName()) + && !TYPE_DELETE.equals(type) + && !TYPE_PASSWORD.equals(type)) { + if ("pending".equals(status) + && assigned.isEmpty()) { + // 待分配无人:抢单 + g.setItem(19, mk( + Material.GOLD_INGOT, + "§6抢单", + "§7接取此工单")); + } + if (assigned.equals(p.getName()) + && ("processing".equals(status) + || "replied".equals(status))) { + // 我在处理:标记完结/上报异常 + g.setItem(19, mk( + Material.LIME_DYE, + "§a标记完结", + "§7上报处理完成")); + g.setItem(20, mk( + Material.REDSTONE, + "§c上报异常", + "§7报告处理中的问题")); + } + } + + g.setItem(22, mk(Material.ARROW, + "§7返回")); + + ItemStack info = g.getItem(10); + if (info != null) { + ItemMeta im = info.getItemMeta(); + if (im != null) { + im.setCustomModelData(tid); + info.setItemMeta(im); + } + } + p.openInventory(g); + } + + + public void openAdminList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_ADMIN); + fillBg(g); + g.setItem(45, mk(Material.EMERALD, + "§a邀请服务商")); + g.setItem(46, mk(Material.BARRIER, + "§c移除服务商")); + g.setItem(47, mk(Material.CLOCK, + "§e服务商列表")); + g.setItem(48, mk(Material.NETHER_STAR, + "§e直接派单", + "§7管理员直接创建并派单")); + List> all = + new ArrayList<>(); + all.addAll(plugin.getDb() + .getTicketsByStatus("pending")); + all.addAll(plugin.getDb() + .getTicketsByStatus("processing")); + all.addAll(plugin.getDb() + .getTicketsByStatus("replied")); + all.addAll(plugin.getDb() + .getTicketsByStatus("completed")); + all.addAll(plugin.getDb() + .getTicketsByStatus("resolved")); + all.addAll(plugin.getDb() + .getTicketsByStatus("closed")); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + + public void openProviderPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§d§l服务商面板"); + fillBg(g); + g.setItem(10, mk(Material.CHEST, + "§e抢单大厅", + "§7查看可接工单")); + g.setItem(12, mk(Material.CLOCK, + "§b我的工单", + "§7我正在处理的工单")); + g.setItem(14, mk(Material.PAPER, + "§6历史工单", + "§7已完成的工单")); + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openGrabList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l抢单大厅"); + fillBg(g); + List> pending = + plugin.getDb() + .getTicketsByStatus( + "pending"); + List> list = + new ArrayList<>(); + for (Map t : pending) { + if (sv(t, "assigned_to").isEmpty()) { + list.add(t); + } + } + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyAssigned(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§b§l我处理中的工单"); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByProvider( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + // ========== 业务 ========== + + public int submitTicket(Player p, + String type, String title, + String desc) { + int price = PRICES + .getOrDefault(type, 5); + int id = plugin.getDb().createTicket( + type, "pending", "normal", + p.getName(), title, desc, + price, ""); + if (id > 0) { + if (TYPE_DELETE.equals(type) + || TYPE_PASSWORD.equals(type)) { + plugin.getDb().updateTicketField( + id, "assigned_to", "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage( + "§a§l[工单] §f工单 #" + + id + " 已提交," + + "等待管理员处理"); + } else { + p.sendMessage( + "§a§l[工单] §f创建成功!" + + " #§e" + id + + " 奖励: §e" + + price + "积分"); + } + notifyAdmins(p.getName(), id); + } else { + p.sendMessage("§c创建失败"); + } + return id; + } + + + public void adminCreateAndAssign( + Player p, String type, + String title, String desc, + String provider, int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + int id = plugin.getDb().createTicket( + type, "processing", "normal", + p.getName(), title, desc, + price, ""); + if (id <= 0) { + p.sendMessage("§c创建失败"); + return; + } + plugin.getDb().assignTicket( + id, provider); + if (price > 0) { + plugin.getDb().updateTicketField( + id, "reward_amount", price); + } + p.sendMessage("§a工单 #" + id + + " 已创建并分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f管理员指派工单 #" + + id + " 给您"); + playAssignedSound(tp); + } + } + + public void grabTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!"pending".equals( + sv(t, "status"))) { + p.sendMessage("§c该工单已被接单"); + return; + } + if (!sv(t, "assigned_to").isEmpty()) { + p.sendMessage("§c该工单已被分配"); + return; + } + plugin.getDb().assignTicket( + tid, p.getName()); + p.sendMessage("§a抢单成功!工单 #" + tid); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f您的工单 #" + + tid + " 已被 " + + p.getName() + + " 接单"); + } + } + + public void assignToProvider(Player p, + int tid, String provider, + int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + plugin.getDb().assignTicket( + tid, provider); + if (price > 0) { + plugin.getDb().updateTicketStatus( + tid, "processing"); + plugin.getDb().updateTicketField( + tid, "reward_amount", price); + } + p.sendMessage("§a工单 #" + tid + + " 已分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f您有新工单: #" + + tid); + playAssignedSound(tp); + } + } + + public void confirmAndScore(Player p, + int tid, int score) { + if (score < 1 || score > 5) { + p.sendMessage("§c评分1-5"); + return; + } + plugin.getDb().confirmTicket( + tid, score); + p.sendMessage("§a工单 #" + tid + + " 已确认,评分: " + score); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已确认完结"); + } + } + settleTicket(tid); + } + + public void completeAsProvider(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "assigned_to") + .equals(p.getName())) { + p.sendMessage("§c这不是你的工单"); + return; + } + plugin.getDb().completeTicket(tid); + p.sendMessage("§a工单 #" + tid + + " 已标记完结,等待确认"); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已处理完成"); + } + } + + public void rejectTicket(Player p, + int tid, String reason) { + plugin.getDb().updateTicketStatus( + tid, "closed"); + plugin.getDb().updateTicketField( + tid, "reject_reason", reason); + p.sendMessage("§c工单 #" + tid + + " 已驳回并关闭"); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 被驳回: " + + reason); + } + } + } + + public void replyTicket(Player p, + int tid, String message) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + String status = sv(t, "status"); + if ("closed".equals(status) + || "withdrawn".equals(status) + || "resolved".equals(status)) { + p.sendMessage("§c该工单已关闭"); + return; + } + plugin.getDb().addTicketReply( + tid, p.getName(), message); + p.sendMessage("§a回复成功"); + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + String sender = p.getName(); + if (!requester.equals(sender)) { + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + if (!assigned.isEmpty() + && !assigned.equals(sender)) { + Player ap = Bukkit.getPlayer( + assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + } + + public void showReplies(Player p, + int tid) { + List> replies = + plugin.getDb() + .getTicketReplies(tid); + if (replies.isEmpty()) { + p.sendMessage("§7暂无回复"); + return; + } + p.sendMessage("§e§l=== 回复记录 ==="); + SimpleDateFormat sdf = + new SimpleDateFormat( + "MM-dd HH:mm"); + for (Map r : replies) { + String sender = sv(r, "sender"); + String msg2 = sv(r, "message"); + long time = ((Number) r.get( + "create_time")).longValue(); + p.sendMessage("§7[" + sdf.format( + new Date(time)) + "] §f" + + sender + "§7: " + msg2); + } + p.sendMessage( + "§e§l================="); + } + + public void withdrawTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "requester") + .equals(p.getName()) + && !adm(p)) { + p.sendMessage("§c无权操作"); + return; + } + plugin.getDb().updateTicketStatus( + tid, "withdrawn"); + p.sendMessage("§a工单 #" + tid + + " 已撤回"); + } + + public void showProviderList(Player p) { + List> list = + plugin.getDb() + .getAllServiceProviders(); + if (list.isEmpty()) { + p.sendMessage("§7暂无服务商"); + return; + } + p.sendMessage( + "§e§l=== 服务商列表 ==="); + for (Map pro : list) { + p.sendMessage("§7- §f" + + pro.get("player_name") + + " §7角色: §e" + + pro.get("role")); + } + p.sendMessage( + "§e§l==================="); + } + + public void autoProcessCompleted() { + long timeoutMs = 10 * 60 * 1000L; + + // 自动处理删号工单(5分钟自动通过) + List> deleteTickets = + plugin.getDb() + .getTicketsByStatus( + "processing"); + for (Map t : deleteTickets) { + String dtype = sv(t, "type"); + if (!TYPE_DELETE.equals(dtype)) continue; + int did = nv(t, "id"); + int dconf = nv(t, "admin_confirmed"); + // 已经处理过了跳过 + if (dconf != 0) continue; + String dasg = sv(t, "assigned_to"); + // 只处理分配给admin的 + if (!"admin".equals(dasg)) continue; + long createTime = ((Number) t.get( + "create_time")).longValue(); + if (System.currentTimeMillis() + - createTime >= 5 * 60 * 1000L) { + processDeleteAccount(did, t); + } + } + + + // 已完结未确认→10分钟自动完结 + List> list = + plugin.getDb() + .getUnconfirmedCompleted( + timeoutMs); + for (Map t : list) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 已自动完结"); + String assigned = + sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) + pp.sendMessage( + "§e§l[工单] §f工单 #" + + id + + " 已自动完结"); + } + settleTicket(id); + } + + // 已回复但10分钟无互动→自动完结 + List> stale = + plugin.getDb() + .getRepliedStaleTickets( + timeoutMs); + for (Map t : stale) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 超时自动完结"); + settleTicket(id); + } + } + + + + // ========== GUI点击 ========== + public boolean handleClick(Player p, + String title, int slot) { + + if (title.equals(T_TICKET_MAIN)) { + if (slot == 10) openCreate(p); + else if (slot == 12) + openMyTickets(p); + else if (slot == 14) + openMyTickets(p); + else if (slot == 16) { + if (adm(p)) openAdminList(p); + else p.sendMessage("§c权限不足"); + } + else if (slot == 18) { + if (plugin.getDb() + .isServiceProvider(p.getName())) { + openProviderPanel(p); + } else { + p.sendMessage("§c权限不足"); + } + } + else if (slot == 22) + plugin.getGui().openMain(p); + return true; + } + + if (title.equals(T_TICKET_CREATE)) { + if (slot == 31) { + openMain(p); + return true; + } + String type = null; + if (slot == 10) type = TYPE_BUG; + else if (slot == 11) type = TYPE_HELP; + else if (slot == 12) type = TYPE_REPORT; + else if (slot == 13) type = TYPE_APPLY; + else if (slot == 14) type = TYPE_OTHER; + else if (slot == 20) type = TYPE_DELETE; + else if (slot == 21) type = TYPE_PASSWORD; + if (type != null) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_TITLE; + plugin.getChatInput() + .getState(p) + .ticketType = type; + if (TYPE_DELETE.equals(type)) { + p.sendMessage("§e请描述删号原因:"); + } else if (TYPE_PASSWORD.equals(type)) { + p.sendMessage("§e请描述需要协助的内容:"); + } else { + p.sendMessage("§e请输入工单标题:"); + } + } + return true; + } + + + if (title.equals(T_TICKET_MY)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals(T_TICKET_DETAIL)) { + return handleClickDetail(p, slot); + } + + if (title.equals(T_TICKET_ADMIN)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot == 45) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_INVITE_PROVIDER; + p.sendMessage("§e输入服务商玩家名:"); + return true; + } + if (slot == 46) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REMOVE_PROVIDER; + p.sendMessage("§e输入要移除的服务商:"); + return true; + } + if (slot == 47) { + showProviderList(p); + return true; + } + if (slot == 48) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ADMIN_TYPE; + p.sendMessage("§e选择工单类型:"); + p.sendMessage("§7bug/求助/举报/申请/其他"); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§d§l服务商面板")) { + if (slot == 22) { + plugin.getGui().openMain(p); + return true; + } + if (slot == 10) openGrabList(p); + else if (slot == 12) openMyAssigned(p); + else if (slot == 14) openMyTickets(p); + return true; + } + + if (title.equals("§6§l抢单大厅")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§b§l我处理中的工单")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + return false; + } + + private boolean handleClickDetail(Player p, + int slot) { + if (slot == 22) { + openMyTickets(p); + return true; + } + int id = gtFromDetail(p); + if (id <= 0) return true; + Map t = + plugin.getDb().getTicket(id); + if (t == null) return true; + + String st = sv(t, "status"); + int conf = nv(t, "admin_confirmed"); + String asg = sv(t, "assigned_to"); + + // 回复按钮 + if (slot == 15) { + if (!"closed".equals(st) + && !"withdrawn".equals(st) + && !"resolved".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPLY; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入回复内容:"); + } + return true; + } + + // 查看回复记录 + if (slot == 16) { + showReplies(p, id); + return true; + } + + // slot 19 + if (slot == 19) { + // 管理员:分配工单 + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ASSIGN; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage( + "§e输入: 服务商名 价格"); + return true; + } + // 管理员:取消指派(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + cancelTicket(p, id); + openDetail(p, id); + return true; + } + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + Map tk = + plugin.getDb().getTicket(id); + String tkType = sv(tk, "type"); + if (TYPE_DELETE.equals(tkType)) { + approveDeleteTicket(p, id); + return true; + } + plugin.getDb().completeTicket(id); + p.sendMessage("§a工单 #" + id + + " 已标记完结"); + openDetail(p, id); + return true; + } + } + // 服务商:抢单 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && "pending".equals(st) + && asg.isEmpty()) { + grabTicket(p, id); + openDetail(p, id); + return true; + } + // 服务商:标记完结 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + completeAsProvider(p, id); + openDetail(p, id); + return true; + } + return true; + } + + // slot 20 + if (slot == 20) { + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:自己接手(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 服务商:上报异常 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPORT_ISSUE; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入异常描述:"); + return true; + } + return true; + } + + // slot 21:驳回 + if (slot == 21) { + if (adm(p) && !"closed".equals(st) + && !"resolved".equals(st) + && !"withdrawn".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REJECT; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入驳回原因:"); + } + return true; + } + + return true; + } + + // ========== 聊天输入 ========== + + public boolean handleChatInput(Player p, + String msg, + ChatInputManager.InputType type) { + switch (type) { + case TICKET_TITLE: { + plugin.getChatInput() + .getState(p) + .ticketTitle = msg; + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return true; + } + case TICKET_DESC: { + String tt = plugin.getChatInput() + .getState(p).ticketType; + String tl = plugin.getChatInput() + .getState(p).ticketTitle; + plugin.getChatInput().reset(p); + submitTicket(p, tt, tl, msg); + return true; + } + case TICKET_SCORE: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + Map tk = + plugin.getDb() + .getTicket(tid2); + String st = tk != null + ? sv(tk, "status") + : ""; + if ("completed".equals(st)) { + plugin.getChatInput() + .reset(p); + confirmAndScore( + p, tid2, sc); + } else { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c工单尚未处理完成"); + } + } catch ( + NumberFormatException e) { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return true; + } + case TICKET_REJECT: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + plugin.getChatInput().reset(p); + rejectTicket(p, tid2, msg); + return true; + } + case TICKET_ASSIGN: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + plugin.getChatInput().reset(p); + assignToProvider(p, tid2, + prov, price); + runSync(() -> + openDetail(p, tid2)); + return true; + } + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return true; + } + plugin.getDb() + .addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return true; + } + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .isServiceProvider( + tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return true; + } + plugin.getDb() + .removeServiceProvider( + tgt); + p.sendMessage( + "§a已移除 " + tgt); + return true; + } + default: + return false; + } + } + + // ========== 私有 ========== + + private void settleTicket(int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) return; + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + ConfigManager cfg = + plugin.getConfig2(); + if (reward > 0) { + plugin.getDb().addPoints( + requester, reward); + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f获得 " + + reward + + " 积分奖励"); + } + } + if (!assigned.isEmpty()) { + int pts = (int) (score + * cfg.providerPointsPerScore); + if (pts > 0) { + plugin.getDb().addPoints( + assigned, pts); + } + double money = + cfg.baseEconomyReward + + score + * cfg.providerEconomyPerScore; + boolean ok = false; + try { + if (plugin.getEconomy() != null) { + OfflinePlayer op = + Bukkit.getOfflinePlayer( + assigned); + plugin.getEconomy() + .depositPlayer( + op, money); + ok = true; + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 经济失败: " + + e.getMessage()); + } + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) { + pp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 确认完结"); + if (pts > 0) + pp.sendMessage("§7积分: §e" + + pts); + if (ok) + pp.sendMessage("§7经济: §a$" + + String.format( + "%.0f", money)); + } + } + } + + private void notifyAdmins(String creator, + int tid) { + String tag = + plugin.getConfig2().adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§e§l[工单] §f" + creator + + " 提交了新工单 #" + + tid); + } + } + } + + private void playAssignedSound(Player p) { + for (int i = 0; i < 3; i++) { + final int d = i * 15; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline()) { + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + }, d); + } + } + + private void runSync(Runnable task) { + Bukkit.getScheduler() + .runTask(plugin, task); + } + + private boolean adm(Player p) { + return p.getScoreboardTags().contains( + plugin.getConfig2().adminTag); + } + + private void fillBg(Inventory g) { + ItemStack glass = mk( + Material.GRAY_STAINED_GLASS_PANE, + " "); + for (int i = 0; i < g.getSize(); i++) { + if (g.getItem(i) == null + || g.getItem(i).getType() + == Material.AIR) { + g.setItem(i, glass); + } + } + } + + private void fillList(Inventory g, + List> list) { + if (list.isEmpty()) { + g.setItem(22, mk(Material.BARRIER, + "§7暂无工单")); + return; + } + int slot = 0; + for (Map t : list) { + if (slot >= 45) break; + g.setItem(slot, makeItem(t)); + slot++; + } + } + /** + * 执行删号流程 + */ + /** + * 执行删号流程 + */ + public void processDeleteAccount(int tid, + Map t) { + String requester = sv(t, "requester"); + plugin.getDb().updateTicketStatus( + tid, "resolved"); + plugin.getDb().updateTicketField(tid, + "admin_confirmed", 1); + Player target = + Bukkit.getPlayer(requester); + if (target != null && target.isOnline()) { + plugin.backupInventory(target); + target.sendMessage( + "§a§l[工单] §f您的删号请求已通过"); + target.sendMessage( + "§7物品已备份,下次登录可取回"); + target.kickPlayer( + "§a删号成功,物品已备份。\n" + + "请等待5分钟后重新注册取回物品。"); + } else { + // 离线:仅标记,不操作数据库 + plugin.getLogger().info( + "[Sdf1_login] " + requester + + " 删号工单已通过,等待上线执行"); + } + // 延迟执行删号 + final String name = requester; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + plugin.getDb().deleteUser(name); + plugin.getLogger().info( + "[Sdf1_login] " + name + + " 删号工单已执行"); + }, 100L); + } + + /** + * 管理员手动批准删号 + */ + public void approveDeleteTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!TYPE_DELETE.equals(sv(t, "type"))) { + p.sendMessage("§c这不是删号工单"); + return; + } + p.sendMessage("§a工单 #" + tid + + " 已批准,正在执行删号..."); + processDeleteAccount(tid, t); + } + + /** + * 管理员取消工单(服务商异常后) + */ + /** + * 管理员取消工单(解除服务商绑定,管理员可自行处理) + */ + public void cancelTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + // 解除服务商绑定,状态回退到pending + plugin.getDb().updateTicketField( + tid, "assigned_to", ""); + plugin.getDb().updateTicketStatus( + tid, "pending"); + p.sendMessage("§a工单 #" + tid + + " 已取消指派,回到待分配状态"); + // 通知原服务商 + String assigned = sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player ap = Bukkit.getPlayer(assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 已被管理员取消指派"); + } + } + } + + + /** + * 服务商查看历史工单 + */ + public void openProviderHistory(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l我的历史工单"); + fillBg(g); + List> all = + plugin.getDb() + .getTicketsByProviderAll( + p.getName()); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + private ItemStack makeItem( + Map t) { + int id = nv(t, "id"); + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = + sv(t, "requester"); + String title = sv(t, "title"); + int reward = nv(t, "reward_amount"); + Material mat; + switch (status) { + case "processing": + mat = Material.YELLOW_DYE; + break; + case "replied": + mat = Material.CYAN_DYE; + break; + case "completed": + mat = Material.LIME_DYE; + break; + case "resolved": + mat = Material.LIME_DYE; + break; + case "closed": + mat = Material.GRAY_DYE; + break; + case "rejected": + mat = Material.RED_DYE; + break; + case "withdrawn": + mat = Material.GRAY_DYE; + break; + default: + mat = Material.ORANGE_DYE; + break; + } + ItemStack item = new ItemStack(mat); + ItemMeta im = item.getItemMeta(); + if (im != null) { + im.setDisplayName("§e#" + id + + " " + (title.isEmpty() + ? type : title)); + List lore = + new ArrayList<>(); + lore.add("§7类型: §f" + type); + lore.add("§7状态: " + + stxt(status)); + lore.add("§7提交者: §f" + + requester); + lore.add("§7奖励: §e" + reward + + " 积分"); + lore.add(""); + lore.add("§7点击查看详情"); + im.setLore(lore); + im.setCustomModelData(id); + item.setItemMeta(im); + } + return item; + } + + private int gt(ItemStack item) { + if (item == null) return -1; + ItemMeta im = item.getItemMeta(); + if (im == null) return -1; + if (im.hasCustomModelData()) + return im.getCustomModelData(); + return -1; + } + + private int gtFromDetail(Player p) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(10); + return gt(it); + } + + private String stxt(String status) { + switch (status) { + case "pending": + return "§e待处理"; + case "processing": + return "§b处理中"; + case "replied": + return "§9已回复"; + case "completed": + return "§a待确认"; + case "resolved": + return "§a已完结"; + case "closed": + return "§7已关闭"; + case "rejected": + return "§c已驳回"; + case "withdrawn": + return "§7已撤回"; + default: + return "§7" + status; + } + } + + private String sv(Map m, + String key) { + Object v = m.get(key); + return v != null + ? String.valueOf(v) : ""; + } + + private int nv(Map m, + String key) { + Object v = m.get(key); + return v instanceof Number + ? ((Number) v).intValue() : 0; + } + + private ItemStack mk(Material mat, + String name, String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) { + List list = + new ArrayList<>(); + for (String l : lore) { + if (!l.isEmpty()) + list.add(l); + } + if (!list.isEmpty()) + im.setLore(list); + } + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/UpdateChecker.java b/backup/20260605_152529/src/main/java/Sdf1_login/UpdateChecker.java new file mode 100644 index 0000000..c5c01bb --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/UpdateChecker.java @@ -0,0 +1,523 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; + +public class UpdateChecker { + + private final Main plugin; + private final String currentVersion; + + // ===== 仓库信息 ===== + private static final String GITHUB_OWNER = + "ypsdf1"; + private static final String GITHUB_REPO = + "sdf1_login"; + private static final String GITEE_OWNER = + "nihaoshidifu"; + private static final String GITEE_REPO = + "sdf1_login"; + + + // API 地址 + private static final String GITHUB_API = + "https://api.github.com/repos/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + + "/tags"; + private static final String GITEE_API = + "https://gitee.com/api/v5/repos/" + + GITEE_OWNER + "/" + + GITEE_REPO + + "/tags"; + + // 仓库页面链接(给用户看的) + private static final String GITHUB_LINK = + "https://github.com/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + "/releases"; + private static final String GITEE_LINK = + "https://gitee.com/" + + GITEE_OWNER + "/" + + GITEE_REPO + "/releases"; + + + // 单次请求超时(毫秒) + private static final int TIMEOUT_MS = 3000; + + // 熔断阈值 + private static final int TRIP_THRESHOLD = 5; + + // ===== 熔断状态(内存态,重启自动清除) ===== + private volatile boolean circuitTripped = + false; + private volatile int failCount = 0; + + // ===== 自动检测禁用(持久化到文件) ===== + private volatile boolean autoCheckDisabled = + false; + private final File disabledFile; + + public UpdateChecker(Main plugin) { + this.plugin = plugin; + this.currentVersion = + plugin.getDescription() + .getVersion(); + this.disabledFile = new File( + plugin.getDataFolder(), + "update_disabled.dat"); + loadDisabledState(); + } + + // ========== 持久化禁用状态 ========== + + private void loadDisabledState() { + if (disabledFile.exists()) { + try (BufferedReader br = + new BufferedReader( + new FileReader( + disabledFile))) { + String line = br.readLine(); + if (line != null) { + autoCheckDisabled = + "true".equals( + line.trim()); + } + } catch (Exception ignored) { + } + } + } + + private void saveDisabledState() { + try (PrintWriter pw = + new PrintWriter( + new FileWriter( + disabledFile))) { + pw.println(autoCheckDisabled + ? "true" : "false"); + } catch (Exception ignored) { + } + } + + // ========== 公开方法 ========== + + /** + * onEnable 调用(异步静默) + * 熔断或禁用则直接跳过 + */ + public void checkOnEnable() { + if (autoCheckDisabled) return; + if (circuitTripped) return; + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doCheck(null, true)); + } + + /** + * /sdf1_login update 调用(异步) + * 忽略熔断和禁用状态,强制检测 + * 只有拿到版号才解除熔断 + */ + public void checkUpdate( + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> + doCheck(sender, + false)); + } + + // ========== 核心检测逻辑 ========== + private void doCheck(CommandSender sender, + boolean isAuto) { + + if (isAuto) { + if (autoCheckDisabled + || circuitTripped) { + return; + } + } + + // 通道1:GitHub + CheckResult gh = + tryChannel(GITHUB_API, + "GitHub", GITHUB_LINK); + + // 通道2:Gitee + CheckResult ge = + tryChannel(GITEE_API, + "Gitee", GITEE_LINK); + + // 选择成功的结果 + CheckResult chosen = null; + String failMsg = null; + + if (gh.isSuccess()) { + chosen = gh; + } else if (ge.isSuccess()) { + chosen = ge; + } else { + // 双通道均失败 + failCount++; + // 拼接两个通道的失败原因 + StringBuilder sb = + new StringBuilder(); + if (gh.error != null) { + sb.append("§7GitHub: §c") + .append(gh.error); + } + if (ge.error != null) { + if (sb.length() > 0) + sb.append("\n"); + sb.append("§7Gitee: §c") + .append(ge.error); + } + if (failCount >= TRIP_THRESHOLD) { + circuitTripped = true; + plugin.getLogger().warning( + "[Sdf1_login] 更新检测" + + "已熔断,连续失败" + + failCount + "次"); + } + if (sender != null) { + final String msg = + sb.toString(); + final boolean tripped = + circuitTripped; + final int fc = failCount; + Bukkit.getScheduler() + .runTask(plugin, () -> { + sender.sendMessage( + "§c§l[更新] " + + "检测失败"); + sender.sendMessage(msg); + sender.sendMessage( + "§e失败次数: " + + fc + "/" + + TRIP_THRESHOLD); + if (tripped) { + sender.sendMessage( + "§c已熔断," + + "请重启服务器" + + "重置"); + } + }); + } + return; + } + + // 至少一个通道成功 + failCount = 0; + + // 检查禁用信号 + if (isDisableSignal(chosen.version)) { + autoCheckDisabled = true; + saveDisabledState(); + plugin.getLogger().info( + "[Sdf1_login] 远程版本信号" + + " \"" + chosen.version + + "\",自动更新检测已禁用"); + if (sender != null) { + Bukkit.getScheduler() + .runTask(plugin, () -> + sender.sendMessage( + "§e[更新] 远程已" + + "禁用自动" + + "更新检测")); + } + return; + } + + // 手动调用:成功才解除熔断和禁用 + if (!isAuto) { + if (circuitTripped) { + circuitTripped = false; + } + if (autoCheckDisabled) { + autoCheckDisabled = false; + saveDisabledState(); + } + } + + final String src = chosen.source; + final String lnk = chosen.link; + final String rv = chosen.version; + + Bukkit.getScheduler() + .runTask(plugin, () -> { + if (sender == null) return; + + sender.sendMessage( + "§e[更新] 检测成功" + + " via: §a" + src); + sender.sendMessage( + "§e[更新] 本地版本:" + + " §f" + + currentVersion); + sender.sendMessage( + "§e[更新] 远程版本:" + + " §f" + rv); + + if (isNewer(rv, + currentVersion)) { + sender.sendMessage( + "§c[更新] 发现新" + + "版本: " + rv); + sender.sendMessage( + "§a请前往仓库下载" + + "更新:"); + sender.sendMessage( + "§a" + lnk); + } else { + sender.sendMessage( + "§a[更新] 已是" + + "最新版本"); + } + + // 显示另一通道的失败原因 + // (仅信息,不影响结果) + CheckResult other = + (src.equals("GitHub")) + ? ge : gh; + if (!other.isSuccess() + && other.error != null) { + sender.sendMessage( + "§7[更新] " + + other.source + + "不可用: " + + other.error); + } + }); + } + + + // ========== 禁用信号判断 ========== + + private boolean isDisableSignal( + String version) { + if (version == null) return false; + String v = version.trim().toLowerCase(); + return "999".equals(v) + || "关闭".equals(v) + || "false".equals(v) + || "禁用".equals(v); + } + + // ========== HTTP 请求(带超时) ========== + + // ========== 检测结果 ========== + + private static class CheckResult { + final String version; + final String source; + final String link; + final String error; + + CheckResult(String version, + String source, + String link, + String error) { + this.version = version; + this.source = source; + this.link = link; + this.error = error; + } + + boolean isSuccess() { + return version != null; + } + } + +// ========== HTTP 请求 ========== + + private CheckResult tryChannel( + String apiUrl, + String sourceName, + String link) { + HttpURLConnection conn = null; + try { + URL url = new URL(apiUrl); + conn = (HttpURLConnection) + url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; " + + "Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) " + + "Chrome/120.0.0.0 Safari/537.36"); + conn.setRequestProperty( + "Accept", + "*/*"); + conn.setRequestProperty( + "Accept-Language", + "zh-CN,zh;q=0.9"); + conn.setConnectTimeout(TIMEOUT_MS); + conn.setReadTimeout(TIMEOUT_MS); + conn.setInstanceFollowRedirects(true); + + int code = conn.getResponseCode(); + + if (code == 404) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 未找到 (HTTP 404)"); + } + if (code == 403) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 被限流 (HTTP 403)"); + } + if (code != 200) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": HTTP " + code); + } + + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn + .getInputStream())); + StringBuilder sb = + new StringBuilder(); + String line; + while ((line = reader.readLine()) + != null) { + sb.append(line); + } + reader.close(); + + String tag = + parseTagName(sb.toString()); + if (tag == null) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 无法解析版本号"); + } + + return new CheckResult(tag, + sourceName, link, null); + + } catch (java.net + .SocketTimeoutException e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": 连接超时"); + } catch (java.net.ConnectException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 连接被拒绝"); + } catch (java.net + .UnknownHostException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": DNS解析失败"); + } catch (Exception e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": " + + e.getMessage()); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + + + /** + * 从 JSON 中提取 tag_name + */ + private String parseTagName(String json) { + // tags API 返回数组: [{"name":"v1.1",...}] + // releases API 返回对象: {"tag_name":"v1.1"} + + // 尝试 tag_name(releases格式) + int idx = + json.indexOf("\"tag_name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + // 尝试 "name"(tags格式) + idx = json.indexOf("\"name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + return null; + } + + private String cleanTag(String tag) { + if (tag.startsWith("v") + || tag.startsWith("V")) { + tag = tag.substring(1); + } + return tag.isEmpty() ? null : tag; + } + + + // ========== 版本比较 ========== + + private boolean isNewer(String remote, + String local) { + String[] r = remote.split("\\."); + String[] l = local.split("\\."); + int len = Math.max(r.length, l.length); + for (int i = 0; i < len; i++) { + int ri = i < r.length + ? parseNum(r[i]) : 0; + int li = i < l.length + ? parseNum(l[i]) : 0; + if (ri > li) return true; + if (ri < li) return false; + } + return false; + } + + private int parseNum(String s) { + try { + return Integer.parseInt( + s.replaceAll("[^0-9]", "")); + } catch (Exception e) { + return 0; + } + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/UserGroupManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/UserGroupManager.java new file mode 100644 index 0000000..adaf907 --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/UserGroupManager.java @@ -0,0 +1,370 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.*; +import java.util.Base64; +import java.util.concurrent.ConcurrentHashMap; + +public class UserGroupManager { + + private final Main plugin; + private Connection db; + private final Map groupConfigs = new ConcurrentHashMap<>(); + + public UserGroupManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadGroupConfigs(); + startExpiryChecker(); + } + + // ===== DB ===== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(plugin.getDataFolder(), "usergroup.db"); + db = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_config (" + + "group_name TEXT PRIMARY KEY," + + "display_name TEXT DEFAULT ''," + + "display_color TEXT DEFAULT '§f'," + + "display_emoji TEXT DEFAULT ''," + + "priority INTEGER DEFAULT 0," + + "is_permanent INTEGER DEFAULT 1," + + "duration_minutes INTEGER DEFAULT 0," + + "pack_url TEXT DEFAULT ''," + + "pack_hash TEXT DEFAULT ''," + + "pack_prompt TEXT DEFAULT 'Custom Resource Pack'," + + "pack_size INTEGER DEFAULT 0)"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_member (" + + "player_name TEXT NOT NULL," + + "group_name TEXT NOT NULL," + + "added_by TEXT DEFAULT 'system'," + + "added_time INTEGER DEFAULT 0," + + "expiry_time INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, group_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException("[UserGroup] DB init failed: " + e.getMessage(), e); + } + } + + // ===== 配置加载 ===== + + public void loadGroupConfigs() { + groupConfigs.clear(); + File dir = new File(plugin.getDataFolder(), "用户组"); + if (!dir.exists()) dir.mkdirs(); + File[] files = dir.listFiles((d, n) -> n.endsWith(".txt")); + if (files == null) return; + + for (File f : files) { + try { + List lines = java.nio.file.Files.readAllLines( + f.toPath(), StandardCharsets.UTF_8); + UserGroupConfig cfg = parseGroupConfig(lines); + if (cfg != null) { + groupConfigs.put(cfg.name, cfg); + saveGroupConfigToDB(cfg); + } + } catch (Exception e) { + plugin.getLogger().warning("[UserGroup] Failed to load: " + f.getName()); + } + } + } + + private UserGroupConfig parseGroupConfig(List lines) { + UserGroupConfig cfg = new UserGroupConfig(); + boolean inBlock = false; + + for (String raw : lines) { + String l = raw; + + // 块注释 + if (l.contains("")) { inBlock = false; continue; } + if (l.contains("/*")) { inBlock = true; continue; } + if (l.contains("*/")) { inBlock = false; continue; } + if (inBlock) continue; + + // 行注释 + l = l.trim(); + if (l.isEmpty()) continue; + if (l.startsWith("#") || l.startsWith("//")) continue; + + // 行内注释 + int h = l.indexOf('#'); + int s = l.indexOf("//"); + if (h >= 0) l = l.substring(0, h).trim(); + if (s >= 0 && (h < 0 || s < h)) l = l.substring(0, s).trim(); + if (l.isEmpty()) continue; + + // 解析键值对 + String key = l; + String val = ""; + int idx = l.indexOf('\uff1a'); + if (idx < 0) idx = l.indexOf(':'); + if (idx >= 0) { + key = l.substring(0, idx).trim(); + val = l.substring(idx + 1).trim(); + if (val.startsWith("\"") && val.endsWith("\"")) + val = val.substring(1, val.length() - 1); + } + + switch (key) { + case "组名": + case "name": cfg.name = val; break; + case "显示名": + case "display": cfg.displayName = val; break; + case "颜色": + case "color": cfg.displayColor = val; break; + case "符号": + case "emoji": cfg.displayEmoji = val; break; + case "优先级": + case "priority": + try { cfg.priority = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "类型": + case "type": + cfg.permanent = val.equalsIgnoreCase("永久") || val.equalsIgnoreCase("permanent"); + break; + case "时长": + case "duration": + try { cfg.durationMinutes = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "资源包URL": + case "pack_url": cfg.packUrl = val; break; + case "资源包Hash": + case "pack_hash": cfg.packHash = val; break; + case "资源包提示": + case "pack_prompt": cfg.packPrompt = val; break; + case "资源包大小": + case "pack_size": + try { cfg.packSize = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "本地文件": + case "local_file": cfg.localFile = val; break; + } + } + + if (cfg.name == null || cfg.name.isEmpty()) return null; + return cfg; + } + + private void saveGroupConfigToDB(UserGroupConfig cfg) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_config " + + "(group_name, display_name, display_color, display_emoji," + + " priority, is_permanent, duration_minutes," + + " pack_url, pack_hash, pack_prompt, pack_size)" + + " VALUES (?,?,?,?,?,?,?,?,?,?,?)"); + ps.setString(1, cfg.name); + ps.setString(2, cfg.displayName); + ps.setString(3, cfg.displayColor); + ps.setString(4, cfg.displayEmoji); + ps.setInt(5, cfg.priority); + ps.setInt(6, cfg.permanent ? 1 : 0); + ps.setInt(7, cfg.durationMinutes); + ps.setString(8, cfg.packUrl); + ps.setString(9, cfg.packHash); + ps.setString(10, cfg.packPrompt); + ps.setInt(11, cfg.packSize); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 玩家操作 ===== + + public boolean addPlayer(String player, String groupName, String addedBy, long durationMinutes) { + UserGroupConfig cfg = groupConfigs.get(groupName); + if (cfg == null) return false; + long now = System.currentTimeMillis(); + long expiry = cfg.permanent ? 0 : now + durationMinutes * 60 * 1000; + if (!cfg.permanent && durationMinutes > 0) expiry = now + durationMinutes * 60 * 1000; + else if (!cfg.permanent) expiry = now + cfg.durationMinutes * 60 * 1000; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_member " + + "(player_name, group_name, added_by, added_time, expiry_time)" + + " VALUES (?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, groupName); + ps.setString(3, addedBy); + ps.setLong(4, now); + ps.setLong(5, expiry); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + Player p = plugin.getServer().getPlayerExact(player); + if (p != null) sendResourcePack(p); + return true; + } + + public boolean removePlayer(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + int rows = ps.executeUpdate(); + ps.close(); + return rows > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public List> getPlayerGroups(String player) { + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM user_group_member WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + long now = System.currentTimeMillis(); + while (rs.next()) { + Map row = new LinkedHashMap<>(); + row.put("group_name", rs.getString("group_name")); + row.put("added_by", rs.getString("added_by")); + long expiry = rs.getLong("expiry_time"); + row.put("expiry_time", expiry); + row.put("expired", expiry > 0 && expiry <= now); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public UserGroupConfig getHighestGroup(String player) { + List> groups = getPlayerGroups(player); + long now = System.currentTimeMillis(); + UserGroupConfig best = null; + for (Map g : groups) { + long expiry = (long) g.get("expiry_time"); + if (expiry > 0 && expiry <= now) continue; + String name = (String) g.get("group_name"); + UserGroupConfig cfg = groupConfigs.get(name); + if (cfg != null && (best == null || cfg.priority > best.priority)) { + best = cfg; + } + } + return best; + } + + public boolean isPlayerInGroup(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + boolean exists = ps.executeQuery().next(); + ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ===== 过期自动移除 ===== + + private void startExpiryChecker() { + new BukkitRunnable() { + @Override + public void run() { + checkExpiry(); + } + }.runTaskTimerAsynchronously(plugin, 1200L, 1200L); // 每分钟检查 + } + + private void checkExpiry() { + try { + long now = System.currentTimeMillis(); + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, group_name FROM user_group_member " + + "WHERE expiry_time > 0 AND expiry_time <= ?"); + ps.setLong(1, now); + ResultSet rs = ps.executeQuery(); + List expired = new ArrayList<>(); + while (rs.next()) { + expired.add(new String[]{ + rs.getString("player_name"), + rs.getString("group_name")}); + } + rs.close(); ps.close(); + + for (String[] e : expired) { + PreparedStatement del = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + del.setString(1, e[0]); + del.setString(2, e[1]); + del.executeUpdate(); + del.close(); + plugin.getLogger().info("[UserGroup] 过期移除: " + e[0] + " -> " + e[1]); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 资源包分发 ===== + + public void sendResourcePack(Player p) { + UserGroupConfig cfg = getHighestGroup(p.getName()); + if (cfg == null || cfg.packUrl == null || cfg.packUrl.isEmpty()) return; + + String url = cfg.packUrl; + String prompt = cfg.packPrompt != null + ? cfg.packPrompt : "Custom Resource Pack"; + + plugin.getServer().getScheduler().runTaskLater(plugin, () -> { + try { + p.setResourcePack(url, new byte[0], prompt, false); + plugin.getLogger().info("[UserGroup] 资源包已发送: " + + p.getName() + " -> " + cfg.displayName); + } catch (Exception e) { + plugin.getLogger().warning( + "[UserGroup] 资源包发送失败: " + e.getMessage()); + } + }, 40L); + } + + public Map getGroupConfigs() { + return groupConfigs; + } + + // ===== 配置类 ===== + + public static class UserGroupConfig { + public String name; + public String displayName = ""; + public String displayColor = "§f"; + public String displayEmoji = ""; + public int priority = 0; + public boolean permanent = true; + public int durationMinutes = 0; + public String packUrl = ""; + public String packHash = ""; + public String packPrompt = "Custom Resource Pack"; + public int packSize = 0; + public String localFile = ""; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/VerificationManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/VerificationManager.java new file mode 100644 index 0000000..3fc4ceb --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/VerificationManager.java @@ -0,0 +1,158 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.function.Consumer; + +public class VerificationManager { + + private final Main plugin; + + public VerificationManager(Main plugin) { + this.plugin = plugin; + } + + public boolean isBedrockPlayer(Player p) { + String uuid = p.getUniqueId().toString(); + return uuid.contains("f851ba4") + || uuid.startsWith("00000000-0000-0000"); + } + + public boolean isOnlineMode() { + return Bukkit.getServer().getOnlineMode(); + } + + public void verifyPremiumAsync(Player player, + Consumer callback) { + Thread t = new Thread(() -> doVerify(player, callback), + "Sdf1_login-PremiumVerify"); + t.setDaemon(true); + t.start(); + } + + private void doVerify(Player player, + Consumer callback) { + String name = player.getName(); + String uuid = player.getUniqueId().toString(); + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证开始 ======"); + plugin.getLogger().info( + "[Sdf1_login] 玩家: " + name); + plugin.getLogger().info( + "[Sdf1_login] 当前UUID: " + uuid); + + try { + UUID offlineUuid = UUID.nameUUIDFromBytes( + ("OfflinePlayer:" + name) + .getBytes(StandardCharsets.UTF_8)); + String offlineStr = offlineUuid.toString(); + + plugin.getLogger().info( + "[Sdf1_login] 离线UUID: " + offlineStr); + + boolean isOfflineUuid = + player.getUniqueId().equals(offlineUuid); + + plugin.getLogger().info( + "[Sdf1_login] UUID匹配离线格式: " + + isOfflineUuid); + + if (!isOfflineUuid) { + plugin.getLogger().info( + "[Sdf1_login] 结论: 正版玩家"); + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(true); + return; + } + + plugin.getLogger().info( + "[Sdf1_login] UUID为离线格式," + + "查询Mojang API..."); + UUID mojangUuid = fetchMojangUuid(name); + + if (mojangUuid != null) { + plugin.getLogger().info( + "[Sdf1_login] Mojang返回UUID: " + + mojangUuid); + plugin.getLogger().info( + "[Sdf1_login] 结论: 盗版客户端" + + "使用了正版名字"); + } else { + plugin.getLogger().info( + "[Sdf1_login] Mojang无此名字"); + plugin.getLogger().info( + "[Sdf1_login] 结论: 纯离线玩家"); + } + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(false); + + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 验证异常: " + + e.getMessage()); + callback.accept(false); + } + } + + private UUID fetchMojangUuid(String name) { + HttpURLConnection conn = null; + try { + URL url = new URL( + "https://api.mojang.com/users/profiles/minecraft/" + + name); + conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + conn.setRequestMethod("GET"); + + int code = conn.getResponseCode(); + plugin.getLogger().info( + "[Sdf1_login] Mojang API响应: " + code); + + if (code == 200) { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn.getInputStream(), + StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + + String json = sb.toString(); + String uuidStr = json.replaceAll( + ".*\"id\"\\s*:\\s*\"([^\"]+)\".*", + "$1"); + plugin.getLogger().info( + "[Sdf1_login] Mojang原始UUID: " + + uuidStr); + + String formatted = uuidStr.replaceFirst( + "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", + "$1-$2-$3-$4-$5"); + return UUID.fromString(formatted); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] Mojang查询异常: " + + e.getMessage()); + } finally { + if (conn != null) conn.disconnect(); + } + return null; + } +} diff --git a/backup/20260605_152529/src/main/java/Sdf1_login/WelcomeManager.java b/backup/20260605_152529/src/main/java/Sdf1_login/WelcomeManager.java new file mode 100644 index 0000000..68bd3ef --- /dev/null +++ b/backup/20260605_152529/src/main/java/Sdf1_login/WelcomeManager.java @@ -0,0 +1,163 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class WelcomeManager { + + private final Main plugin; + private final Random random = new Random(); + private final List registerMsgs = + new ArrayList<>(); + private final List loginMsgs = + new ArrayList<>(); + + public WelcomeManager(Main plugin) { + this.plugin = plugin; + loadMessages(); + } + + public void loadMessages() { + registerMsgs.clear(); + loginMsgs.clear(); + File f = new File( + plugin.getDataFolder(), + "欢迎仪式.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("注册") + || t.equals("注册:")) { + section = "register"; + continue; + } + if (t.equals("登录") + || t.equals("登录:")) { + section = "login"; + continue; + } + // 支持 = 和 : 两种分隔 + if (section.isEmpty()) { + if (t.startsWith("注册") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + registerMsgs.add( + colorize(v)); + continue; + } + if (t.startsWith("登录") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + loginMsgs.add( + colorize(v)); + continue; + } + continue; + } + if (!t.isEmpty()) { + if (section.equals("register")) + registerMsgs.add( + colorize(t)); + else + loginMsgs.add( + colorize(t)); + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[欢迎] 加载失败: " + + e.getMessage()); + } + plugin.getLogger().info( + "[欢迎] 加载完成: 注册=" + + registerMsgs.size() + + " 登录=" + loginMsgs.size()); + } + + public void onRegister(Player p) { + sendRandom(p, registerMsgs, "注册"); + } + + public void onLogin(Player p) { + sendRandom(p, loginMsgs, "登录"); + } + + private void sendRandom(Player p, + List msgs, String tag) { + if (msgs.isEmpty()) return; + String msg = msgs.get( + random.nextInt(msgs.size())); + msg = msg.replace("{player}", + p.getName()); + final String finalMsg = msg; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> Bukkit + .broadcastMessage( + finalMsg), 10L); + } + + private String colorize(String msg) { + return msg.replace("&", "\u00a7"); + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets + .UTF_8))) { + pw.println("# 欢迎仪式配置"); + pw.println("# &为颜色代码"); + pw.println("# {player} = 玩家名"); + pw.println(); + pw.println("注册:"); + pw.println("&a&l欢迎 &f{player} &a加入服务器!&7[注册]"); + pw.println("&6&l{player} &e已完成注册,欢迎入驻!&7[注册]"); + pw.println("&b&l新成员 &f{player} &b已就位!&7[注册]"); + pw.println(); + pw.println("登录:"); + pw.println("&a&l欢迎回来 &f{player}&a!&7[登录]"); + pw.println("&e&l{player} &e已登录,继续冒险吧!&7[登录]"); + pw.println("&b&l{player} &b回来了!&7[登录]"); + } catch (IOException ignored) { + } + } +} diff --git a/backup/20260605_152529/src/main/resources/plugin.yml b/backup/20260605_152529/src/main/resources/plugin.yml new file mode 100644 index 0000000..f6868d9 --- /dev/null +++ b/backup/20260605_152529/src/main/resources/plugin.yml @@ -0,0 +1,104 @@ +name: Sdf1_login +version: 1.0 +main: Sdf1_login.Main +api-version: '1.21' +commands: + sdf1_login: + description: Sdf1_login主命令 + usage: /sdf1_login + reg: + description: 注册 + usage: /reg <密码> + login: + description: 登录 + usage: /login <密码> + l: + description: 登录快捷版 + usage: /l <密码> + 注册: + description: 注册(中文) + usage: /注册 <密码> + 登录: + description: 登录(中文) + usage: /登录 <密码> + 玩家信息: + description: 打开面板(中文) + usage: /玩家信息 + 改密码: + description: 修改密码(中文) + usage: /改密码 <旧密码> <新密码> <确认新密码> + 设置邮箱: + description: 设置邮箱(中文) + usage: /设置邮箱 <邮箱地址> + 撤销: + description: 撤销操作(中文) + usage: /撤销 + 删除: + description: 删除玩家(中文) + usage: /删除 <玩家> <密码> + 找回密码: + description: 找回密码(中文) + usage: /找回密码 + 签到: + description: 每日签到(中文) + usage: /签到 + #oa: + # description: 申请删除账号 + # usage: /oa [玩家名] + 删除账号: + description: 申请删除账号(中文) + usage: /删除账号 [玩家名] + 通过: + description: 批准OA请求(中文) + usage: /通过 <编号> + 拒绝: + description: 拒绝OA请求(中文) + usage: /拒绝 <编号> [理由] + accept: + description: 批准OA请求 + usage: /accept <编号> + deny: + description: 拒绝OA请求 + usage: /deny <编号> [理由] + email: + description: 绑定邮箱 + usage: /email <邮箱> + 绑定: + description: 绑定邮箱(中文) + usage: /绑定 <邮箱> + 绑定邮箱: + description: 绑定邮箱 + usage: /绑定邮箱 + chat: + description: 聊天过滤管理 + usage: /chat + recycle: + description: 打开垃圾回收站 + usage: /recycle + 垃圾清理: + description: 手动清理掉落物 + usage: /垃圾清理 + menu: + description: 雪球菜单开关控制 + usage: /menu <开/关> + pvp : + description: PVP榜单 + usage: /pvp + cypay : + description: 草原探险债券 + usage: /cypay + printer : + description: 打印债券流水 + usage: /printer + shop : + description: 游戏商人 + usage: /shop + 商店: + description: 打开商店 + usage: /商店 + #testorder: + # description: 测试订单中心 + # usage: /testorder + protect: + description: 区域保护 + usage: /protect \ No newline at end of file diff --git a/backup/20260605_185500/CheckInManager.java.backup b/backup/20260605_185500/CheckInManager.java.backup new file mode 100644 index 0000000..b8e612c --- /dev/null +++ b/backup/20260605_185500/CheckInManager.java.backup @@ -0,0 +1,305 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class CheckInManager { + + private final Main plugin; + + public CheckInManager(Main plugin) { + this.plugin = plugin; + } + + // ===== 今日是否已签到 ===== + public boolean isCheckedInToday( + String name) { + String last = (String) plugin.getDb() + .getField(name, + "last_checkin_date"); + String today = new SimpleDateFormat( + "yyyy-MM-dd") + .format(new Date()); + return today.equals(last); + } + + // ===== 签到 ===== + // ===== 签到 ===== + public String checkIn(Player p) { + String name = p.getName(); + if (isCheckedInToday(name)) { + return plugin.getConfig2() + .msg("checkin_already"); + } + + Object streakObj = plugin.getDb() + .getField(name, "checkin_streak"); + int streak = streakObj != null + ? ((Number) streakObj).intValue() + : 0; + + String lastDate = (String) plugin.getDb() + .getField(name, "last_checkin_date"); + String today = new SimpleDateFormat("yyyy-MM-dd") + .format(new Date()); + + if (lastDate != null && !lastDate.isEmpty()) { + try { + java.util.Date lastD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(lastDate); + java.util.Date todayD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(today); + long diff = todayD.getTime() + - lastD.getTime(); + if (diff > 86400000L * 2) { + // 间隔超过2天,重置连续天数 + streak = 1; + } else if (diff > 86400000L) { + // 间隔1-2天(连续签到),天数+1 + streak++; + } + // 间隔<=1天(同一天),不改变连续天数 + } catch (Exception ignored) { + } + } else { + // 首次签到 + streak = 1; + } + + double multi = 1.0; + if (streak >= 21) multi = 1.5; + else if (streak >= 14) multi = 1.3; + else if (streak >= 7) multi = 1.1; + + int basePoints = + plugin.getConfig2().checkinPoints; + int points = (int) (basePoints * multi); + + plugin.getDb().addPoints(name, points); + plugin.getDb().setField(name, + "checkin_streak", streak); + plugin.getDb().setField(name, + "last_checkin_date", today); + + Object totalObj = plugin.getDb() + .getField(name, "total_checkin_days"); + int total = totalObj != null + ? ((Number) totalObj).intValue() + : 0; + plugin.getDb().setField(name, + "total_checkin_days", total + 1); + + // ★ 债券盲盒奖励(6参数版,写入流水)★ + int bondMin = + plugin.getConfig2().checkinBondMin; + int bondMax = + plugin.getConfig2().checkinBondMax; + if (bondMin <= 0) bondMin = 1; + if (bondMax < bondMin) bondMax = bondMin; + int bondReward = bondMin + + new java.util.Random() + .nextInt(bondMax - bondMin + 1); + plugin.getBonds().addBonds(name, bondReward, + "daily_sign", "", "签到系统", + "每日签到连续" + streak + "天"); + + return plugin.getConfig2() + .msg("checkin_success") + .replace("{points}", + String.valueOf(points)) + .replace("{streak}", + String.valueOf(streak)) + .replace("{multi}", + String.valueOf(multi)) + + " §e+" + bondReward + "债券"; + } + + // ===== 获取连续天数 ===== + public int getStreak(String name) { + Object val = plugin.getDb() + .getField(name, + "checkin_streak"); + if (val instanceof Number) { + return ((Number) val) + .intValue(); + } + return 0; + } + // 标准化日期 + private String normalizeDate(String input) { + String s = input.trim().replace("/", "-"); + if (s.matches("\\d{8}")) { + return s.substring(0, 4) + "-" + + s.substring(4, 6) + "-" + + s.substring(6, 8); + } + return s; + } + // ===== 补签入口 ===== + public String backCheckIn( + Player p, String dateStr) { + String name = p.getName(); + String normalized = + normalizeDate(dateStr); + + // 严格日期验证 + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd"); + sdf.setLenient(false); + java.util.Date target; + try { + target = sdf.parse(normalized); + } catch (Exception ex) { + return "§c无效日期: " + dateStr; + } + String reformat = sdf.format(target); + if (!reformat.equals(normalized)) { + return "§c无效日期: " + dateStr; + } + + String today = sdf.format(new Date()); + if (normalized.compareTo(today) >= 0) { + return "§c今天不需要补签"; + } + + String threeDaysAgo = getPastDate(3); + if (normalized.compareTo( + threeDaysAgo) < 0) { + return "§c只能补签3天内" + + "(最多到 " + threeDaysAgo + ")"; + } + + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + boolean alreadyBacked = false; + if (!existing.isEmpty()) { + for (String d : existing.split(",")) { + if (normalizeDate(d.trim()) + .equals(normalized)) { + alreadyBacked = true; + break; + } + } + } + if (alreadyBacked) { + return "§c该日期已补签: " + normalized; + } + + Object normalCheck = + plugin.getDb().getField( + name, "last_checkin_date"); + String lastCheckin = normalCheck != null + ? normalCheck.toString() : ""; + if (lastCheckin.equals(normalized)) { + return "§c该日期已签到: " + normalized; + } + + // ★ 补签消耗:3债券 + 10积分 + // 积分不够时:用 9债券(3倍)代替 ★ + int bondCost = 3; + int pointCost = 10; + int bondAltCost = 9; // 3倍债券替代 + + int myBonds = plugin.getBonds() + .getBonds(name); + Object ptsObj = plugin.getDb() + .getField(name, "points"); + int myPoints = ptsObj != null + ? ((Number) ptsObj).intValue() + : 0; + + + // 方案A:3债券 + 10积分 + boolean canA = myBonds >= bondCost + && myPoints >= pointCost; + // 方案B:9债券(积分不够时) + boolean canB = myBonds >= bondAltCost + && myPoints < pointCost; + + if (!canA && !canB) { + if (myPoints < pointCost + && myBonds < bondAltCost) { + return "§c补签费用不足" + + "(需3债券+10积分," + + "或9债券代替)" + + " 当前: " + + myBonds + "债券 " + + myPoints + "积分"; + } + if (myBonds < bondCost) { + return "§c债券不足(需" + + bondCost + "枚)"; + } + return "§c积分不足(需" + + pointCost + "分)"; + } + + // 执行扣款 + if (canA) { + // 方案A:3债券 + 10积分 + plugin.getBonds().deductBonds( + name, bondCost, + "admin_deduct", "", + "补签系统", "补签扣款"); + plugin.getDb().deductPoints( + name, pointCost); + } else { + // 方案B:9债券代替 + plugin.getBonds().deductBonds( + name, bondAltCost, + "admin_deduct", "", + "补签系统", "补签扣款(债券替代)"); + } + + doBackCheckIn(p, normalized); + + String costMsg = canA + ? bondCost + "债券+" + pointCost + "积分" + : bondAltCost + "债券(替代)"; + return "§a补签成功!日期: " + + normalized + + " §7消耗: " + costMsg; + } + + + + // ===== 执行补签 ===== + // ===== 执行补签 ===== + public void doBackCheckIn( + Player p, String dateStr) { + String name = p.getName(); + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + String updated = existing.isEmpty() + ? dateStr + : existing + "," + dateStr; + plugin.getDb().setField(name, + "backcheck_dates", updated); + int streak = getStreak(name); + plugin.getDb().setField(name, + "checkin_streak", streak + 1); + // ★ 补签不发放奖励,只记录 + } + + // ===== 获取N天前日期 ===== + private String getPastDate( + int daysAgo) { + Calendar cal = + Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, + -daysAgo); + return new SimpleDateFormat( + "yyyy-MM-dd") + .format(cal.getTime()); + } +} \ No newline at end of file diff --git a/backup/20260605_185500/DatabaseManager.java.backup b/backup/20260605_185500/DatabaseManager.java.backup new file mode 100644 index 0000000..cb2b8f7 --- /dev/null +++ b/backup/20260605_185500/DatabaseManager.java.backup @@ -0,0 +1,1758 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.logging.Logger; + +public class DatabaseManager { + + private Connection db; + private final File dataFolder; + private final Logger logger; + + public DatabaseManager(File dataFolder) { + this.dataFolder = dataFolder; + this.logger = Logger.getLogger("Sdf1_login"); + } + + // ==================== 初始化 ==================== + + public void init() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(dataFolder, + "login.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 用户表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "users (" + + "player_name TEXT PRIMARY KEY," + + "password_hash TEXT NOT NULL," + + "password_salt TEXT NOT NULL," + + "email TEXT DEFAULT ''," + + "register_time INTEGER DEFAULT 0," + + "last_login_time INTEGER DEFAULT 0," + + "total_online_time INTEGER DEFAULT 0," + + "last_online_check INTEGER DEFAULT 0," + + "is_logged_in INTEGER DEFAULT 0," + + "blocks_broken INTEGER DEFAULT 0," + + "blocks_placed INTEGER DEFAULT 0," + + "gift_stage INTEGER DEFAULT 0," + + "gift_claimed TEXT DEFAULT ''," + + "invite_code TEXT DEFAULT ''," + + "invited_by TEXT DEFAULT ''," + + "monthly_invite_count INTEGER DEFAULT 0," + + "invite_month TEXT DEFAULT ''," + + "points INTEGER DEFAULT 0," + + "checkin_streak INTEGER DEFAULT 0," + + "last_checkin_date TEXT DEFAULT ''," + + "total_checkin_days INTEGER DEFAULT 0," + + "temp_password TEXT DEFAULT ''," + + "temp_pw_expire INTEGER DEFAULT 0," + + "temp_pw_used INTEGER DEFAULT 0," + + "ip_address TEXT DEFAULT ''," + + "tasks_completed TEXT DEFAULT ''," + + "premium_uuid TEXT DEFAULT ''," + + "register_ip TEXT DEFAULT ''," + + "register_type TEXT DEFAULT 'manual'," + + "backcheck_dates TEXT DEFAULT ''" + + ")"); + + + // 安全添加 users 缺失列 + safeAdd(st, "tasks_completed", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_password", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_pw_expire", + "INTEGER DEFAULT 0"); + safeAdd(st, "temp_pw_used", + "INTEGER DEFAULT 0"); + safeAdd(st, "ip_address", + "TEXT DEFAULT ''"); + safeAdd(st, "premium_uuid", + "TEXT DEFAULT ''"); + safeAdd(st, "register_ip", + "TEXT DEFAULT ''"); + safeAdd(st, "register_type", + "TEXT DEFAULT 'manual'"); + + // ===== 背包备份表:安全迁移 ===== + // 检查旧表是否存在(用 inventory_data 列判断) + boolean hasOldTable = false; + try { + ResultSet rs = st.executeQuery( + "SELECT inventory_data " + + "FROM inventory_backups " + + "LIMIT 1"); + hasOldTable = true; + rs.close(); + } catch (SQLException e) { + hasOldTable = false; + } + + if (hasOldTable) { + // 旧表存在,迁移数据到新表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups_new (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + // 迁移旧数据 + try { + st.execute("INSERT INTO " + + "inventory_backups_new " + + "(player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience) " + + "SELECT player_name, " + + "inventory_data, " + + "armor_data, " + + "extra_data, " + + "player_level, " + + "player_exp " + + "FROM inventory_backups"); + } catch (SQLException ignored) { + } + st.execute("DROP TABLE " + + "inventory_backups"); + st.execute("ALTER TABLE " + + "inventory_backups_new " + + "RENAME TO " + + "inventory_backups"); + } else { + // 无旧表或已迁移,直接创建 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + } + + // 安全报警表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "security_alerts (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "alert_ip TEXT NOT NULL," + + "alert_time INTEGER NOT NULL," + + "alert_date TEXT NOT NULL," + + "notified INTEGER DEFAULT 0," + + "notified_time INTEGER DEFAULT 0)"); + // PVP区域表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "name TEXT NOT NULL UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER DEFAULT 0" + + ")"); + +// PVP击杀总榜表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT NOT NULL," + + "region_name TEXT NOT NULL," + + "kills INTEGER DEFAULT 0," + + "deaths INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, region_name)" + + ")"); + + +// 菜单图标表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "menu_icons (" + + "player_name TEXT PRIMARY KEY," + + "icon_b64 TEXT DEFAULT ''," + + "item_name TEXT DEFAULT ''," + + "save_time INTEGER DEFAULT 0" + + ")"); + + // 工单表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "tickets (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "type TEXT NOT NULL," + + "status TEXT DEFAULT 'pending'," + + "priority TEXT DEFAULT 'normal'," + + "requester TEXT NOT NULL," + + "assigned_to TEXT DEFAULT ''," + + "title TEXT DEFAULT ''," + + "description TEXT DEFAULT ''," + + "reward_amount INTEGER DEFAULT 0," + + "score INTEGER DEFAULT 0," + + "target_player TEXT DEFAULT ''," + + "create_time INTEGER NOT NULL," + + "update_time INTEGER DEFAULT 0," + + "complete_time INTEGER DEFAULT 0," + + "admin_confirmed INTEGER DEFAULT 0," + + "reject_reason TEXT DEFAULT ''" + + ")"); + + // 工单回复表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "ticket_replies (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "ticket_id INTEGER NOT NULL," + + "sender TEXT NOT NULL," + + "message TEXT NOT NULL," + + "create_time INTEGER NOT NULL" + + ")"); + + // 服务商表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "service_providers (" + + "player_name TEXT PRIMARY KEY," + + "role TEXT DEFAULT 'waiter'," + + "active INTEGER DEFAULT 1," + + "join_time INTEGER DEFAULT 0" + + ")"); + // 垃圾回收站物品表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "items (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "item_data TEXT DEFAULT ''," + + "amount INTEGER DEFAULT 1," + + "create_time INTEGER DEFAULT 0" + + ")"); + safeAdd(st, "menu_snowball", + "INTEGER DEFAULT 1"); + st.close(); + logger.info("[Sdf1_login] 数据库初始化完成"); + } catch (Exception e) { + throw new RuntimeException( + "[Sdf1_login] DB初始化失败: " + + e.getMessage(), e); + } + } + + + private void safeAdd(Statement st, + String col, String def) { + try { + st.executeUpdate("ALTER TABLE users " + + "ADD COLUMN " + col + " " + def); + } catch (SQLException ignored) { + } + } + + public void close() { + try { + if (db != null && !db.isClosed()) + db.close(); + } catch (Exception ignored) { + } + } +// ==================== 菜单图标 ==================== + + public void saveMenuIcon(String name, + String iconB64, String itemName) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO menu_icons " + + "(player_name, icon_b64, " + + "item_name, save_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, iconB64); + ps.setString(3, itemName); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT icon_b64 FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + String b64 = null; + if (rs.next()) { + b64 = rs.getString("icon_b64"); + } + rs.close(); + ps.close(); + return b64; + } catch (SQLException e) { + return null; + } + } + + public void deleteMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM menu_icons " + + "WHERE player_name=? " + + "AND icon_b64 IS NOT NULL " + + "AND icon_b64 != '' " + + "LIMIT 1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + // ==================== 用户操作 ==================== + + public boolean userExists(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public void createUser(String name, String hash, + String salt) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO users " + + "(player_name, " + + "password_hash, " + + "password_salt, " + + "register_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, hash); + ps.setString(3, salt); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ========== 临时密码管理 ========== + + public void clearTempPassword(String name) { + setField(name, "temp_password", null); + setField(name, "temp_pw_expire", 0); + setField(name, "temp_pw_used", 1); + } + + public void clearAllTempPasswords() { + try { + Statement st = db.createStatement(); + st.executeUpdate( + "UPDATE users SET " + + "temp_password=NULL," + + "temp_pw_expire=0," + + "temp_pw_used=1 " + + "WHERE temp_password " + + "IS NOT NULL"); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPlayerIPByName( + String name) { + Object ip = getField(name, "ip"); + return ip != null + ? String.valueOf(ip) : null; + } + + public boolean checkPasswordOrTemp( + String name, String hash) { + // 先检查主密码 + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String mainHash = + rs.getString("password_hash"); + rs.close(); + ps.close(); + if (hash.equals(mainHash)) + return true; + } else { + rs.close(); + ps.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + // 再检查临时密码 + try { + PreparedStatement ps2 = db.prepareStatement( + "SELECT temp_password, " + + "temp_pw_expire, " + + "temp_pw_used " + + "FROM users " + + "WHERE player_name=?"); + ps2.setString(1, name); + ResultSet rs2 = ps2.executeQuery(); + if (rs2.next()) { + String tempHash = + rs2.getString( + "temp_password"); + long expire = + rs2.getLong( + "temp_pw_expire"); + int used = + rs2.getInt( + "temp_pw_used"); + rs2.close(); + ps2.close(); + if (tempHash != null + && !tempHash.isEmpty() + && hash.equals(tempHash) + && System.currentTimeMillis() + < expire + && used != 1) { + return true; + } + } else { + rs2.close(); + ps2.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + public Map getUser(String name) { + Map r = + new LinkedHashMap<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + r.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return r; + } + + public void setField(String name, String field, + Object val) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE users SET " + field + + "=? " + + "WHERE player_name=?"); + if (val instanceof Integer) + ps.setInt(1, (Integer) val); + else if (val instanceof Long) + ps.setLong(1, (Long) val); + else + ps.setString(1, + String.valueOf(val)); + ps.setString(2, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Object getField(String name, + String field) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT " + field + + " FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + Object val = rs.next() + ? rs.getObject(1) : null; + rs.close(); + ps.close(); + return val; + } catch (SQLException e) { + return null; + } + } + + public boolean checkPassword(String name, + String hash) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String stored = + rs.getString("password_hash"); + rs.close(); + ps.close(); + return hash.equals(stored); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + + public boolean isUsingTempPassword(String name) { + String h = (String) getField(name, + "temp_password"); + return h != null && !h.isEmpty(); + } + + public void setLoggedIn(String name, + boolean val) { + setField(name, "is_logged_in", + val ? 1 : 0); + } + + public void addPoints(String name, int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + setField(name, "points", curVal + amount); + } + + public boolean deductPoints(String name, + int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + if (curVal < amount) return false; + setField(name, "points", + curVal - amount); + return true; + } + public List> + getAllUsers() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM users"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + public List> + getTicketsByProviderAll( + String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deleteUser(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void recordIP(String name, String ip) { + if (ip != null && !ip.isEmpty()) { + setField(name, "ip_address", ip); + } + } + + // ==================== IP查询 ==================== + + /** + * 统计指定IP注册的账号数量 + */ + public int countIPAccounts(String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address=?"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count; + } catch (SQLException e) { + return 0; + } + } + + /** + * 获取指定IP下所有账号详情 + */ + public List> + getAccountDetailsByIP(String ip) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, " + + "register_time " + + "FROM users " + + "WHERE ip_address=? " + + "ORDER BY register_time"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("register_time", + rs.getLong("register_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 背包备份 ==================== + + public void saveInventoryBackup(String name, + String contents, String armor, + String extra, int level, + double experience) { + try { + PreparedStatement del = + db.prepareStatement( + "DELETE FROM " + + "inventory_backups " + + "WHERE player_name=?"); + del.setString(1, name); + del.executeUpdate(); + del.close(); + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO " + + "inventory_backups" + + " (player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience, " + + "save_time) " + + "VALUES " + + "(?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, contents); + ps.setString(3, armor); + ps.setString(4, extra); + ps.setInt(5, level); + ps.setDouble(6, experience); + ps.setLong(7, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public String[] loadInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time " + + "DESC LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void deleteInventoryBackup( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM inventory_backups " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + boolean has = rs.next(); + rs.close(); + ps.close(); + return has; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +// ==================== PVP区域 ==================== + + public void savePVPRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions " + + "(name, world, " + + "x1, y1, z1, " + + "x2, y2, z2, " + + "create_time) " + + "VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map + getPVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getAllPVPRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + db.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deletePVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +// ==================== PVP击杀 ==================== + + public int getPVPKills( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int kills = 0; + if (rs.next()) + kills = rs.getInt("kills"); + rs.close(); + ps.close(); + return kills; + } catch (SQLException e) { + return 0; + } + } + + public int getPVPDeaths( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int deaths = 0; + if (rs.next()) + deaths = rs.getInt("deaths"); + rs.close(); + ps.close(); + return deaths; + } catch (SQLException e) { + return 0; + } + } + + public void addPVPKill( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,1,0) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "kills=kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void addPVPDeath( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,0,1) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "deaths=deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getPVPKillTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT player_name, " + + "kills, deaths " + + "FROM pvp_kills " + + "WHERE " + + "region_name=? " + + "AND kills>0 " + + "ORDER BY kills " + + "DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + // ==================== 安全报警 ==================== + + public void recordSecurityAlert( + String playerName, String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO security_alerts " + + "(player_name, alert_ip, " + + "alert_time, alert_date) " + + "VALUES (?,?,?,?)"); + ps.setString(1, playerName); + ps.setString(2, ip); + long now = System.currentTimeMillis(); + ps.setLong(3, now); + ps.setString(4, + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date( + now))); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + public List> + getUnnotifiedAlerts( + String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM security_alerts " + + "WHERE player_name=? " + + "AND notified=0 " + + "ORDER BY alert_time ASC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void markAlertNotified(int alertId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE security_alerts " + + "SET notified=1, " + + "notified_time=? " + + "WHERE id=?"); + ps.setLong(1, + System.currentTimeMillis()); + ps.setInt(2, alertId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + /** + * 获取玩家最近N条背包备份 + */ + public List> + getInventoryBackups(String name, + int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id, save_time " + + "FROM inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time DESC " + + "LIMIT ?"); + ps.setString(1, name); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", + rs.getInt("id")); + row.put("save_time", + rs.getLong("save_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + /** + * 获取指定ID的备份数据 + */ + public String[] getInventoryBackupById( + int backupId) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM inventory_backups " + + "WHERE id=?"); + ps.setInt(1, backupId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + + // ==================== 工单操作 ==================== + + public int createTicket(String type, + String status, String priority, + String requester, String title, + String description, + int rewardAmount, + String targetPlayer) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO tickets " + + "(type, status, priority, " + + "requester, title, " + + "description, " + + "reward_amount, " + + "target_player, " + + "create_time) " + + "VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, type); + ps.setString(2, status); + ps.setString(3, priority); + ps.setString(4, requester); + ps.setString(5, title); + ps.setString(6, description); + ps.setInt(7, rewardAmount); + ps.setString(8, targetPlayer); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ResultSet rs = + ps.getGeneratedKeys(); + int id = -1; + if (rs.next()) id = rs.getInt(1); + rs.close(); + ps.close(); + return id; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + public void updateTicketStatus(int ticketId, + String status) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET status=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, status); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void assignTicket(int ticketId, + String provider) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "assigned_to=?, " + + "status='processing', " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, provider); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void completeTicket(int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='completed', " + + "complete_time=?, " + + "update_time=? " + + "WHERE id=?"); + long now = System.currentTimeMillis(); + ps.setLong(1, now); + ps.setLong(2, now); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void confirmTicket(int ticketId, + int score) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "admin_confirmed=1, " + + "score=?, " + + "status='resolved', " + + "update_time=? " + + "WHERE id=?"); + ps.setInt(1, score); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public void rejectTicketComplete(int ticketId, + String reason) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='rejected', " + + "reject_reason=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, reason); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 更新工单任意字段 + */ + public void updateTicketField(int ticketId, + String field, Object value) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + field + + "=? WHERE id=?"); + if (value instanceof Integer) + ps.setInt(1, (Integer) value); + else if (value instanceof Long) + ps.setLong(1, (Long) value); + else + ps.setString(1, + String.valueOf(value)); + ps.setInt(2, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map getTicket( + int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE id=?"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getTicketsByStatus(String status) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status=? " + + "ORDER BY create_time DESC"); + ps.setString(1, status); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void addTicketReply(int ticketId, + String sender, String message) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO ticket_replies " + + "(ticket_id, sender, " + + "message, create_time) " + + "VALUES (?,?,?,?)"); + ps.setInt(1, ticketId); + ps.setString(2, sender); + ps.setString(3, message); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + // 更新工单状态为已回复 + updateTicketStatus(ticketId, + "replied"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getTicketReplies(int ticketId) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM ticket_replies " + + "WHERE ticket_id=? " + + "ORDER BY create_time ASC"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + /** + * 获取已回复但超时未互动的工单 + */ + public List> + getRepliedStaleTickets( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='replied' " + + "AND update_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByPlayer(String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE requester=? " + + "ORDER BY create_time DESC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByProvider(String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "AND (status='processing' " + + "OR status='replied') " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getUnconfirmedCompleted( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='completed' " + + "AND admin_confirmed=0 " + + "AND complete_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 服务商操作 ==================== + + public void addServiceProvider(String name, + String role) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO " + + "service_providers " + + "(player_name, role, " + + "active, join_time) " + + "VALUES (?,?,1,?)"); + ps.setString(1, name); + ps.setString(2, role); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void removeServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM service_providers " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean isServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM service_providers " + + "WHERE player_name=? " + + "AND active=1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public List> + getAllServiceProviders() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM service_providers " + + "WHERE active=1"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Connection getDb() { + return db; + } +} diff --git a/backup/20260605_185500/Main.java.backup b/backup/20260605_185500/Main.java.backup new file mode 100644 index 0000000..ed02c8c --- /dev/null +++ b/backup/20260605_185500/Main.java.backup @@ -0,0 +1,5568 @@ +package Sdf1_login; + + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerInteractEvent; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataType; +import java.util.Date; +import org.bukkit.command.CommandSender; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.scheduler.BukkitTask; + + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; + + + +public class Main extends JavaPlugin + implements CommandExecutor, Listener, + TabCompleter { + + private DatabaseManager db; + private MenuIconManager menuIconMgr; + private ConfigManager config; + private EmailManager email; + private LoginManager loginMgr; + private AFKManager afk; + private GiftManager gift; + private CheckInManager checkIn; + private InviteManager invite; + private PointsManager points; + public GUIManager gui; + private ChatInputManager chatInput; + private Economy economy; + private VerificationManager verification; + private IPGroupManager ipGroup; + private AccountRequestManager accountRequest; + private TicketManager ticket; + private ChatFilterManager chatFilter; + private GarbageManager garbage; + public RadioManager radio; + // 在 lastActivity 字段后面添加: + // [ADDED] 任务追踪系统 + private QuestTracker questTracker; + // [FIX] 垃圾箱防抖锁(记录上次操作时间戳) + private final Map garbageBusy = + new ConcurrentHashMap<>(); + // [ADDED] 群系检测节流 + private final Map lastBiomeCheck = + new ConcurrentHashMap<>(); + // 在 private WelcomeManager welcome; 字段(若不存在)添加: + private WelcomeManager welcome; + private final Set pendingAdminAuth = + new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + /* + 资源包下载部分控制,不经过RadioDownloadListener的熔断控制 + */ + private RadioDownloadListener radioDL; + private final Set chatInputActive = + ConcurrentHashMap.newKeySet(); + // 补签确认:玩家名 → "日期:总积分" + public final Map + pendingBackCheck = + new HashMap<>(); + public MenuManager menu; + + private final Set loggedIn = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Set needsPasswordChange = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Map joinTime = + new ConcurrentHashMap<>(); + private final Map joinLoc = + new HashMap<>(); + private final Map ipAutoLogin = + new HashMap<>(); + private static final long IP_SKIP_MS = 300000L; + private SalesStatsManager salesStats; + + private final Map lastActivity = + new ConcurrentHashMap<>(); + private CommissionManager commission; + // 余额操作待处理 + private String balanceTarget = null; + private boolean balanceGiveMode = false; + private BondManager bondManager; + private CDKManager cdkManager; + private UserGroupManager userGroupManager; + private CypayCommand cypayCommand; + + public BondManager getBonds() { + return bondManager; + } + + public CDKManager getCDK() { + return cdkManager; + } + + public UserGroupManager getUserGroup() { + return userGroupManager; + } + + public CypayCommand getCypay() { + return cypayCommand; + } + + // 在 Main.java 字段声明区域(bondManager 附近)添加: +// 在 bondManager 字段附近添加: + private BondPrinter bondPrinter; + private OrderManager orderManager; + + public OrderManager getOrderManager() { + return orderManager; + } + + public AreaProtection areaProtection; + + + // 钱包流水查看目标(玩家名 → 查看目标) + private final Map walletViewTarget = + new ConcurrentHashMap<>(); + + // 流水格式化 + private static final java.text.SimpleDateFormat + TX_SDF = new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + private ShopManager shopManager; //商店 + + + public CommissionManager getCommission() { + return commission; + } + + //pvp + private PVPManager pvpManager; + + private static class PwdRollback { + final String hash; + final String salt; + final long time; + + PwdRollback(String h, String s, long t) { + hash = h; + salt = s; + time = t; + } + } + + // 待删除的玩家名(仅保留最后一个) + private String pendingDeleteName = null; + private BukkitRunnable pendingDeleteTask = null; + + private final Map + pwdRollback = + new ConcurrentHashMap<>(); + private final Map menuChatField = + new java.util.concurrent.ConcurrentHashMap<>(); + private final Map menuChatIdx = + new java.util.concurrent.ConcurrentHashMap<>(); + + + public void recordPasswordChange(String name, + String oldHash, String oldSalt) { + pwdRollback.put(name, + new PwdRollback(oldHash, oldSalt, + System.currentTimeMillis())); + } + + // ===== Getters ===== + public DatabaseManager getDb() { + return db; + } + + + public ConfigManager getConfig2() { + return config; + } + + public EmailManager getEmail() { + return email; + } + + public Economy getEconomy() { + return economy; + } + + public LoginManager getLoginMgr() { + return loginMgr; + } + + public AFKManager getAfk() { + return afk; + } + + public GiftManager getGift() { + return gift; + } + + public CheckInManager getCheckIn() { + return checkIn; + } + + public InviteManager getInvite() { + return invite; + } + + public PointsManager getPoints() { + return points; + } + + public GUIManager getGui() { + return gui; + } + + public ChatInputManager getChatInput() { + return chatInput; + } + + public VerificationManager getVerification() { + return verification; + } + + public IPGroupManager getIPGroup() { + return ipGroup; + } + + public AccountRequestManager getAccountRequest() { + return accountRequest; + } + + public TicketManager getTicket() { + return ticket; + } + + public QuestTracker getQuestTracker() { + return questTracker; + } + + public ChatFilterManager getChatFilter() { + return chatFilter; + } + + public GarbageManager getGarbage() { + return garbage; + } + + public Set getLoggedIn() { + return loggedIn; + } + + public Set getNeedsPasswordChange() { + return needsPasswordChange; + } + + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + if (areaProtection != null) { + Player p = e.getPlayer(); + areaProtection.onPlayerOffline( + p.getUniqueId(), p.getName(), ""); + if (areaProtection != null) { + areaProtection.onPlayerQuit(p.getName()); + } + + } + } + + + // public RadioManager radio; + @Override + public void onEnable() { + getDataFolder().mkdirs(); + salesStats = new SalesStatsManager(this); + + // ===== 1. 基础配置 ===== + config = new ConfigManager(getDataFolder()); + config.loadMessages(); + config.loadSmtp(); + config.loadSettings(); + configMgr = new ConfigManager(getDataFolder()); + configMgr.loadSettings(); + configMgr.loadMessages(); + orderManager = new OrderManager(this); + // ===== 2. 数据库 ===== + db = new DatabaseManager(getDataFolder()); + db.init(); + menuIconMgr = new MenuIconManager(this); + + + // ===== 3. 菜单 ===== + menu = new MenuManager(this); + menu.loadMenu(); + + // ===== 4. GUI(依赖 menu) ===== + gui = new GUIManager(this); + shopManager = new ShopManager(this); + Bukkit.getPluginManager().registerEvents(shopManager, this); + // shopManager.loadCategories(); // 构造器内已调用,此处重复 + + getServer().getPluginManager() + .registerEvents(gui, this); + + // ===== 5. 其他管理器 ===== + email = new EmailManager(config); + loginMgr = new LoginManager(this); + afk = new AFKManager(config, this); + gift = new GiftManager(this); + gift.loadStages(); + checkIn = new CheckInManager(this); + invite = new InviteManager(this); + points = new PointsManager(this); + chatInput = new ChatInputManager(); + verification = new VerificationManager(this); + ipGroup = new IPGroupManager(this, + config.maxAccountsPerIP); + accountRequest = + new AccountRequestManager(this); + accountRequest.setMode(config.approvalMode); + accountRequest.setAutoDelayMinutes( + config.autoApproveDelayMinutes); + ticket = new TicketManager(this); + commission = new CommissionManager(this); + questTracker = new QuestTracker(this); + chatFilter = new ChatFilterManager(this); + chatFilter.loadConfig(); + welcome = new WelcomeManager(this); + + // ===== 6. 垃圾箱 ===== + garbage = new GarbageManager(this); + garbage.init(); + garbage.loadConfig(); + garbage.startAutoCleanup(); + + // ===== 7. Radio ===== + radio = new RadioManager(this); + radio.init(); + radioDL = new RadioDownloadListener(this); + getServer().getPluginManager() + .registerEvents(radioDL, this); + + // ===== 铁块电梯 ===== + getServer().getPluginManager() + .registerEvents(new IronBlockElevator(), this); + + // ===== 8. 经济 ===== + setupEconomy(); + + // ===== 9. 注册命令 ===== + if (getCommand("sdf1_login") != null) { + getCommand("sdf1_login") + .setExecutor(this); + getCommand("sdf1_login") + .setTabCompleter(this); + } + if (getCommand("reg") != null) + getCommand("reg").setExecutor(this); + if (getCommand("login") != null) + getCommand("login").setExecutor(this); + if (getCommand("l") != null) + getCommand("l").setExecutor(this); + if (getCommand("签到") != null) + getCommand("签到") + .setExecutor(this); + if (getCommand("recycle") != null) + getCommand("recycle") + .setExecutor(this); + if (getCommand("垃圾清理") != null) + getCommand("垃圾清理") + .setExecutor(this); + /* if (getCommand("oa") != null) + getCommand("oa").setExecutor(this);*/ + if (getCommand("menu") != null) + getCommand("menu").setExecutor(this); + if (getCommand("printer") != null) { + getCommand("printer").setExecutor(this); + getCommand("shop").setExecutor(this); + getCommand("商店").setExecutor(this); + // getCommand("testorder").setExecutor(this); + if (getCommand("printer") != null) { + getCommand("printer").setExecutor(this); + getCommand("shop").setExecutor(this); + getCommand("商店").setExecutor(this); + } +// protect 命令独立注册 + if (getCommand("protect") != null) { + getCommand("protect").setExecutor(this); + getCommand("protect").setTabCompleter(this); + } + } + + // ===== 10. 注册事件 ===== + getServer().getPluginManager() + .registerEvents(this, this); + + // ===== 11. 定时任务 ===== + afk.startCheck(); + startLoginReminder(); + startTimeoutCheck(); + startPasswordReminder(); + startTicketAutoProcess(); + radio.startMainRadio(); + new BukkitRunnable() { + public void run() { + if (questTracker == null) return; + for (Player p : + Bukkit.getOnlinePlayers()) { + if (!isFrozen(p)) + questTracker.onPlayTime( + p.getName(), 30000L); + } + } + }.runTaskTimer(this, 600L, 600L); +// [新增] Printer 文件清理(每小时检测一次) + new BukkitRunnable() { + public void run() { + if (bondPrinter != null) + bondPrinter.cleanupOldFiles(); + } + }.runTaskTimer(this, 1200L, 72000L); + + // 12 ===PVP=== + pvpManager = new PVPManager(this); + getServer().getPluginManager() + .registerEvents(pvpManager, this); +// PVP区域检测定时器 + getServer().getScheduler() + .runTaskTimer(this, + () -> pvpManager.tickRegions(), + 20L, 20L); + setupPVPTab(); + + // 13 ====cypay债券==== + // 债券系统(独立DB) + bondManager = new BondManager(this); + cdkManager = new CDKManager(this); + userGroupManager = new UserGroupManager(this); + cypayCommand = new CypayCommand(this); +/*加载CDK文件 + cdkManager.loadCDKsFromDir();*/ + // cypay 命令 + if (getCommand("cypay") != null) { + getCommand("cypay").setExecutor(cypayCommand); + getCommand("cypay").setTabCompleter(cypayCommand); + } + bondPrinter = new BondPrinter(this); + // cdkManager.loadCDKsFromDir(); +//14 ====商店==== + // ★ 构造器内已加载完毕,此处重复 + // if (shopManager != null) { + // shopManager.loadCategories(); + // } + + configMgr = new ConfigManager(getDataFolder()); + configMgr.loadSettings(); + orderManager = new OrderManager(this); + +// 15 ======区域保护========= + areaProtection = new AreaProtection(this); + getServer().getPluginManager() + .registerEvents(areaProtection, this); + +// 效果清除定时器(只留一个) + getServer().getScheduler().runTaskTimer(this, () -> { + if (areaProtection != null) { + } + }, 20L, 20L); + +// 和平模式独立检测 + getServer().getScheduler().runTaskTimer(this, () -> { + if (areaProtection == null) return; + for (Player p : Bukkit.getOnlinePlayers()) { + AreaProtection.AreaConfig ac = areaProtection.getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null) { + // 无条件打印,不管 peaceMode 是啥 + /* getLogger().info("[和平定时器] " + p.getName() + + " 在[" + ac.name + + "] peaceMode=" + ac.peaceMode + + " 白名单=" + ac.peaceWhitelist);*/ + if (ac.peaceMode) { + areaProtection.banHostilesWithWhitelist(p, ac); + } + } + } + }, 40L, 40L); + areaProtection.startEnforceTask(); + if (areaProtection != null) { + areaProtection.reload(); + areaProtection.startEnforceTask(); + } + + + getLogger().info("§b[Sdf1_login]启动完毕\n§lS欢迎使用sdf1系列插件,如有问题,您可在\nGitHub和Gitee提交反馈"); + getLogger().info("sdf1系列插件包含:\nsdf1自助兑奖插件\nCY_beibao云背包\nsdf1_login登陆插件\nSdf1_game娱乐游戏插件"); + // 大字画 + getLogger().info("\n" + + " ____ _ __ _ _ _ \n" + + " / ___| __| |/ _/ | | | ___ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | | |/ _ \\ / _` | | '_ \\ \n" + + " ___) | (_| | _| | | | (_) | (_| | | | | | \n" + + " |____/ \\__,_|_| |_|___|_|\\___/ \\__, |_|_| |_| \n" + + " ____ _ __ |_____| _ |___/ _ \n" + + " / ___| __| |/ _/ |_ __ | |_ _ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | '_ \\| | | | |/ _` | | '_ \\ \n" + + " ___) | (_| | _| | |_) | | |_| | (_| | | | | | \n" + + " |____/ \\__,_|_| |_| .__/|_|\\__,_|\\__, |_|_| |_|___ _ __ _ \n" + + " _ __ _____ |_|___ _ __ | |___/_ _ / ___| __| |/ _/ |\n" + + " | '_ \\ / _ \\ \\ /\\ / / _ \\ '__| | '_ \\| | | | \\___ \\ / _` | |_| |\n" + + " | |_) | (_) \\ V V / __/ | | |_) | |_| | ___) | (_| | _| |\n" + + " | .__/ \\___/ \\_/\\_/ \\___|_| |_.__/ \\__, | |____/ \\__,_|_| |_|\n" + + " |_| |___/ " + ); + } + + public BondPrinter getBondPrinter() { + return bondPrinter; + } + + public ShopManager getShopManager() { + return shopManager; + } + + private ConfigManager configMgr; + + public ConfigManager getConfigMgr() { + return configMgr; + } + + + private void setupPVPTab() { + if (getCommand("pvp") != null) { + getCommand("pvp") + .setTabCompleter( + (sender, cmd, + alias, args) -> { + List list = + new ArrayList<>(); + if (args.length == 1) { + String[] sub = { + "create", "stats", + "list", "delete", + "tool", "on", "off", + "tempban"}; + for (String s : sub) { + if (s.startsWith( + args[0] + .toLowerCase())) { + list.add(s); + } + } + } else if (args.length == 2) { + String sub = + args[0].toLowerCase(); + if (sub.equals("stats")) { + for (Player pp : Bukkit + .getOnlinePlayers()) { + if (pp.getName() + .toLowerCase() + .startsWith(args[1] + .toLowerCase())) { + list.add(pp.getName()); + } + } + } else if (sub.equals("delete") + || sub.equals("on") + || sub.equals("off") + || sub.equals("tempban")) { + for (String rn : + pvpManager + .getRegionNames()) { + if (rn.toLowerCase() + .startsWith(args[1] + .toLowerCase())) { + list.add(rn); + } + } + } + } + return list; + }); + } + } + + @Override + public void onDisable() { + if (shopManager != null) shopManager.saveAll(); + // 取消未执行的删除任务 + if (pendingDeleteTask != null) { + pendingDeleteTask.cancel(); + pendingDeleteTask = null; + getLogger().info( + "[Sdf1_login] 服务器关闭," + + "取消待删除: " + + pendingDeleteName); + pendingDeleteName = null; + } + if (db != null) db.close(); + if (garbage != null) garbage.close(); + if (questTracker != null) + questTracker.shutdown(); + if (areaProtection != null) { + areaProtection.stopEnforceTask(); + } + for (BukkitTask task : areaProtection.getPendingTasks()) { + task.cancel(); + } + areaProtection.saveWhitelists(); + areaProtection.stopEnforceTask(); + // 取消所有延时清理任务,立即清理 + if (areaProtection != null) { + for (Map.Entry entry + : areaProtection.getPendingClearTasks().entrySet()) { + entry.getValue().cancel(); + // 立即清理效果 + Player p = Bukkit.getPlayer(entry.getKey()); + if (p != null && p.isOnline()) { + List marked = + areaProtection.getPlayerMarkedEffects() + .remove(entry.getKey()); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + } + } + areaProtection.stopEnforceTask(); + areaProtection.saveWhitelists(); + areaProtection.getAlreadyForced().clear(); + + } + } + + private void setupEconomy() { + if (getServer().getPluginManager() + .getPlugin("Vault") == null) return; + RegisteredServiceProvider rsp = + getServer().getServicesManager() + .getRegistration( + Economy.class); + if (rsp != null) + economy = rsp.getProvider(); + } + + // ===== Timers ===== + private void startTimeoutCheck() { + new BukkitRunnable() { + public void run() { + long now = System.currentTimeMillis(); + Iterator> it = + joinTime.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = + it.next(); + if (now - entry.getValue() + > config.loginTimeout + * 1000L) { + Player p = Bukkit.getPlayer( + entry.getKey()); + if (p != null && p.isOnline() + && !loggedIn.contains( + p.getName())) + p.kickPlayer(config.msg( + "login_timeout")); + it.remove(); + } + } + } + }.runTaskTimer(this, 80L, 80L); + } + + private void startLoginReminder() { + new BukkitRunnable() { + public void run() { + Iterator it = + joinTime.keySet().iterator(); + while (it.hasNext()) { + UUID uuid = it.next(); + Player p = Bukkit.getPlayer(uuid); + if (p == null || !p.isOnline()) { + it.remove(); + continue; + } + if (loggedIn.contains( + p.getName())) { + it.remove(); + continue; + } + // 已注册→提示登录,未注册→提示注册 + if (db.userExists(p.getName())) + p.sendMessage(config.msg( + "not_logged_in")); + else + p.sendMessage(config.msg( + "not_registered")); + } + } + }.runTaskTimer(this, 200L, 200L); + } + + private void startPasswordReminder() { + new BukkitRunnable() { + public void run() { + for (Player p : + Bukkit.getOnlinePlayers()) { + if (needsPasswordChange.contains( + p.getName())) { + p.sendTitle("§c§l请修改密码", + "§f使用 /sdf1_login pw 修改密码", + 10, 40, 10); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 1.0f); + } + } + } + }.runTaskTimer(this, 200L, 200L); + } + + private void startTicketAutoProcess() { + new BukkitRunnable() { + public void run() { + if (ticket != null) + ticket.autoProcessCompleted(); + } + }.runTaskTimer(this, 6000L, 6000L); + } + + // ===== Utils ===== + public String getPlayerIP(Player p) { + InetSocketAddress addr = p.getAddress(); + if (addr == null) return null; + return addr.getAddress().getHostAddress(); + } + + + public void resetActivity(Player p) { + lastActivity.put(p.getUniqueId(), + System.currentTimeMillis()); + } + + public boolean canAutoLogin(Player p) { + String ip = getPlayerIP(p); + if (ip == null) return false; + Long last = ipAutoLogin.get(ip); + return last != null + && System.currentTimeMillis() - last + < IP_SKIP_MS; + } + + public void recordIPLogin(Player p) { + String ip = getPlayerIP(p); + if (ip != null) + ipAutoLogin.put(ip, + System.currentTimeMillis()); + } + + public boolean isFrozen(Player p) { + return !loggedIn.contains(p.getName()); + } + private String debugCodePoints(String s) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + if (i > 0) sb.append(" "); + sb.append(String.format("U+%04X", + (int) s.charAt(i))); + } + return sb.toString(); + } + + private boolean isAdmin(CommandSender sender) { + if (sender instanceof Player) + return ((Player) sender) + .getScoreboardTags() + .contains(config.adminTag); + return true; + } + + private void clearPlayer(Player p) { + p.getInventory().clear(); + p.getInventory().setArmorContents( + new ItemStack[4]); + p.getInventory().setExtraContents( + new ItemStack[1]); + p.setLevel(0); + p.setExp(0f); + } + + public void restoreInventory(Player p) { + String name = p.getName(); + try { + String[] backup = + db.loadInventoryBackup(name); + if (backup == null + || !isValidBackup(backup)) { + return; + } + + debugPrintBase64("恢复前", name, + backup[0]); + + ItemStack[] contents = + InventorySerializer + .fromBase64(backup[0]); + if (contents != null) + p.getInventory() + .setContents(contents); + + if (backup[1] != null + && !backup[1].isEmpty()) { + ItemStack[] armor = + InventorySerializer + .fromBase64( + backup[1]); + if (armor != null) + p.getInventory() + .setArmorContents( + armor); + } + + if (backup[2] != null + && !backup[2].isEmpty()) { + ItemStack[] extra = + InventorySerializer + .fromBase64( + backup[2]); + if (extra != null) + p.getInventory() + .setExtraContents( + extra); + } + + int lv = Integer.parseInt( + backup[3]); + if (lv > 0) p.setLevel(lv); + double xp = Double.parseDouble( + backup[4]); + if (xp > 0) + p.setExp((float) xp); + + debugPrintBase64("恢复后", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + + getLogger().info( + "[Sdf1_login] " + name + + " 背包已恢复"); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] 还原背包失败: " + + name); + e.printStackTrace(); + } + } + + + public void backupInventory(Player p) { + String name = p.getName(); + int cnt = countItems(p) + + countArmor(p) + + countExtra(p); + if (cnt == 0) { + getLogger().warning( + "[Sdf1_login] " + name + + " 背包为空,跳过备份"); + return; + } + try { + String invB64 = + InventorySerializer.toBase64( + p.getInventory() + .getContents()); + String armorB64 = + InventorySerializer.toBase64( + p.getInventory() + .getArmorContents()); + String extraB64 = + InventorySerializer.toBase64( + p.getInventory() + .getExtraContents()); + db.saveInventoryBackup(name, invB64, + armorB64, extraB64, + p.getLevel(), p.getExp()); + getLogger().info( + "[Sdf1_login] " + name + + " 背包已备份 (物品:" + + cnt + ")"); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] 背包备份失败: " + + name); + e.printStackTrace(); + } + } + + private void hideInventory(Player p) { + String name = p.getName(); + try { + debugPrintBase64("清空前", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + + // 清空背包(备份已在上次退出时保存) + p.getInventory().clear(); + p.getInventory() + .setArmorContents(null); + p.getInventory() + .setExtraContents(null); + p.setLevel(0); + p.setExp(0); + + debugPrintBase64("清空后", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] hideInventory失败: " + + name); + e.printStackTrace(); + } + } + + private void hideInventoryDelayed(Player p, + int attempt) { + String name = p.getName(); + if (!p.isOnline()) return; + if (!isFrozen(p)) return; + + // 从数据库加载已有备份 + String[] existing = + db.loadInventoryBackup(name); + + // 调试打印 + debugPrintBase64("加入-加载DB", name, + existing != null + ? existing[0] : null); + + // 无有效备份 → 跳过清空背包 + if (existing == null + || !isValidBackup(existing)) { + getLogger().info( + "[Sdf1_login] " + name + + " 无有效备份," + + "跳过清空背包"); + return; + } + + // 有有效备份 → 清空当前背包 + hideInventory(p); + } + + + public void autoLogin(Player p, + String registerType) { + String name = p.getName(); + String ip = getPlayerIP(p); + String uuid = + p.getUniqueId().toString(); + String defaultPwd = "123456aA"; + + if (!db.userExists(name)) { + if (config.maxAccountsPerIP > 0 + && ip != null + && !ipGroup.canRegister(ip)) { + p.kickPlayer( + buildIPKickMessage(p, ip)); + return; + } + if (!"manual".equals( + config.approvalMode)) { + AccountRequestManager.Request req = + accountRequest.createRequest( + name, name, ip); + if (req != null) { + p.kickPlayer( + config.msg("need_approval") + .replace("{id}", + String.valueOf( + req.id))); + return; + } + } + String salt = + PasswordUtils.generateSalt(); + String hash = PasswordUtils.hash( + defaultPwd, salt); + db.createUser(name, hash, salt); + db.setField(name, + "premium_uuid", uuid); + db.setField(name, + "register_ip", ip); + db.setField(name, + "register_type", registerType); + db.recordIP(name, ip); + } + + loggedIn.add(name); + p.setAllowFlight(false); + p.setFlying(false); + + + db.setLoggedIn(name, true); + db.setField(name, + "last_login_time", + System.currentTimeMillis()); + db.setField(name, + "last_online_check", + System.currentTimeMillis()); + db.recordIP(name, ip); + joinTime.remove(p.getUniqueId()); + recordIPLogin(p); + + // 只在有有效备份时恢复 + restoreInventory(p); + + giveMenuSnowball(p); + p.sendMessage("§a[Sdf1_login] §f您已自动登录!"); + if (welcome != null) welcome.onLogin(p); + activateBeibao(p); + pushPendingAlerts(p); + + + } + + private void activateBeibao(Player p) { + try { + org.bukkit.plugin.Plugin cy = + Bukkit.getPluginManager() + .getPlugin("CY_beibao"); + if (cy != null && cy.isEnabled()) + cy.getClass().getMethod( + "onSdf1Activation", + String.class, + int.class, int.class) + .invoke(cy, p.getName(), + 0, 0); + } catch (Exception ignored) { + } + } + + public void triggerSecurityAlert(Player player, + String suspiciousIP) { + String name = player.getName(); + db.recordSecurityAlert(name, suspiciousIP); + Bukkit.broadcastMessage( + "§c§l[安全警告] §e" + name + + " §f账号疑似遭到 §c" + + suspiciousIP + + " §f的盗号尝试"); + String emailAddr = + (String) db.getField(name, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String body = "玩家 " + name + + ":\n您的账号在 " + + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()) + + " 疑似遭到 IP: " + + suspiciousIP + + " 的盗号尝试。\n" + + "建议立即修改密码。"; + final String to = emailAddr; + final String finalBody = body; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> email + .sendTempPassword( + to, name, + finalBody)); + } + } + + public void pushPendingAlerts(Player p) { + String name = p.getName(); + List> alerts = + db.getUnnotifiedAlerts(name); + if (alerts.isEmpty()) return; + p.sendMessage("§c§l========== 安全报警 =========="); + for (Map alert : alerts) { + p.sendMessage("§e时间: §f" + + alert.get("alert_date")); + p.sendMessage("§e可疑IP: §c" + + alert.get("alert_ip")); + db.markAlertNotified( + ((Number) alert.get("id")) + .intValue()); + } + p.sendMessage( + "§c建议使用 §e/sdf1_login pw §c修改密码"); + p.sendMessage( + "§c§l=================================="); + } + + public void handleReset(Player p) { + loginMgr.handleReset(p); + } + + public boolean dailySign(Player p) { + if (checkIn.isCheckedInToday(p.getName())) + return false; + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn(p.getName()); + return true; + } + + public void openTaskPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l任务面板"); + ItemStack gl = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta glm = gl.getItemMeta(); + if (glm != null) { + glm.setDisplayName(" "); + gl.setItemMeta(glm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, gl); + + // 打卡按钮(独立,slot 11) + g.setItem(11, mkItem(Material.COMPASS, + "§b§l群系打卡", + "§7在不同群系打卡完成任务", + "§7打卡范围: 32格去重")); + + // 新人任务(slot 15) + g.setItem(15, mkItem(Material.BOOK, + "§e新人任务", + "§7完成新手阶段获取奖励")); + + // 返回 + g.setItem(22, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + public void openAccountRequestPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§e§l账号请求"); + ItemStack gl = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta glm = gl.getItemMeta(); + if (glm != null) { + glm.setDisplayName(" "); + gl.setItemMeta(glm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, gl); + g.setItem(10, mkItem(Material.BARRIER, + "§c申请删除账号", + "§7申请删除自己名下的账号")); + g.setItem(13, mkItem(Material.PAPER, + "§6提交工单", + "§7提交bug反馈、求助、举报等")); + g.setItem(16, mkItem(Material.REDSTONE, + "§e找回密码", + "§7通过邮箱找回密码")); + g.setItem(22, mkItem(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + private void playSuccessSound(Player p) { + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + + private int countItems(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + private int countArmor(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getArmorContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + private int countExtra(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getExtraContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + /** + * 检查备份是否有效(包含非雪球的真实物品) + */ + public boolean isValidBackup(String[] backup) { + if (backup == null + || backup[0] == null + || backup[0].isEmpty()) { + return false; + } + try { + ItemStack[] items = + InventorySerializer + .fromBase64(backup[0]); + if (items == null) return false; + for (ItemStack item : items) { + if (item == null + || item.getType() + == Material.AIR) + continue; + if (isMenuSnowball(item)) + continue; + return true; + } + } catch (Exception e) { + return false; + } + return false; + } + + /** + * 控制台打印 base64 前10位 + */ + private void debugPrintBase64(String label, + String name, String base64) { + if (base64 == null || base64.isEmpty()) { + getLogger().info( + "[BACKUP_DEBUG] " + label + + " " + name + + ": "); + return; + } + String preview = + base64.length() > 10 + ? base64.substring(0, 10) + : base64; + getLogger().info( + "[BACKUP_DEBUG] " + label + " " + + name + ": " + preview); + } + + private ItemStack mkItem(Material mat, + String name, String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) { + List list = + new ArrayList<>(); + for (String l : lore) + if (!l.isEmpty()) list.add(l); + if (!list.isEmpty()) + im.setLore(list); + } + it.setItemMeta(im); + } + return it; + } + + private String buildIPKickMessage(Player p, + String ip) { + List> accounts = + db.getAccountDetailsByIP(ip); + StringBuilder sb = new StringBuilder(); + sb.append("§c§l======== IP注册已满 ========\n"); + sb.append("§e您的IP §f").append(ip) + .append(" §e下已注册 §f") + .append(accounts.size()) + .append(" §e个账号:\n"); + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm"); + int idx = 1; + for (Map acc : accounts) { + long regTime = ((Number) acc.get( + "register_time")).longValue(); + String dateStr = regTime > 0 + ? sdf.format(new java.util.Date( + regTime)) + : "未知"; + sb.append("§7").append(idx) + .append(". §f") + .append(acc.get("player_name")) + .append(" §7注册于: §e") + .append(dateStr).append("\n"); + idx++; + } + sb.append("§7-----------------------------\n"); + sb.append("§e请使用已有账号登录\n"); + sb.append("§e登录后输入 §f/oa §e提交工单\n"); + sb.append("§c§l============================"); + return sb.toString(); + } + + /** + * 读取备份,返回物品名预览 + */ + private String getBackupItemPreview( + int backupId) { + try { + String[] backup = + db.getInventoryBackupById( + backupId); + if (backup == null + || backup[0] == null + || backup[0].isEmpty()) + return "§7空"; + ItemStack[] items = + InventorySerializer + .fromBase64( + backup[0]); + if (items == null) + return "§7空"; + Map counts = + new LinkedHashMap<>(); + for (ItemStack it : items) { + if (it == null + || it.getType() + == Material.AIR) + continue; + String name = translateMaterial( + it.getType().name()); + counts.merge(name, + it.getAmount(), + Integer::sum); + } + if (counts.isEmpty()) + return "§7空"; + StringBuilder sb = + new StringBuilder(); + int shown = 0; + for (Map.Entry en + : counts.entrySet()) { + if (shown >= 5) { + sb.append(" §7..."); + break; + } + if (shown > 0) + sb.append("§7, "); + sb.append("§f").append(en.getKey()) + .append("§7x") + .append(en.getValue()); + shown++; + } + return sb.toString(); + } catch (Exception e) { + return "§7解析失败"; + } + } + + private String translateMaterial( + String name) { + switch (name) { + case "GRASS_BLOCK": + return "草方块"; + case "OAK_LOG": + return "橡木原木"; + case "OAK_PLANKS": + return "橡木木板"; + case "STONE": + return "石头"; + case "COBBLESTONE": + return "圆石"; + case "DIRT": + return "泥土"; + case "SAND": + return "沙子"; + case "GRAVEL": + return "砂砾"; + case "DIAMOND": + return "钻石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "IRON_INGOT": + return "铁锭"; + case "GOLD_INGOT": + return "金锭"; + case "COAL": + return "煤炭"; + case "COAL_ORE": + return "煤矿石"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "OAK_LEAVES": + return "橡树树叶"; + case "OAK_SAPLING": + return "橡树树苗"; + case "STICK": + return "木棍"; + case "CRAFTING_TABLE": + return "工作台"; + case "FURNACE": + return "熔炉"; + case "CHEST": + return "箱子"; + case "TORCH": + return "火把"; + case "BEDROCK": + return "基岩"; + case "TNT": + return "TNT"; + case "REDSTONE": + return "红石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "LAPIS_LAZULI": + return "青金石"; + case "EMERALD": + return "绿宝石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "OBSIDIAN": + return "黑曜石"; + case "GLOWSTONE": + return "萤石"; + case "ARROW": + return "箭"; + case "BOW": + return "弓"; + case "IRON_SWORD": + return "铁剑"; + case "DIAMOND_SWORD": + return "钻石剑"; + case "IRON_PICKAXE": + return "铁镐"; + case "DIAMOND_PICKAXE": + return "钻石镐"; + case "IRON_AXE": + return "铁斧"; + case "DIAMOND_AXE": + return "钻石斧"; + case "IRON_HELMET": + return "铁头盔"; + case "IRON_CHESTPLATE": + return "铁胸甲"; + case "IRON_LEGGINGS": + return "铁护腿"; + case "IRON_BOOTS": + return "铁靴子"; + case "DIAMOND_HELMET": + return "钻石头盔"; + case "DIAMOND_CHESTPLATE": + return "钻石胸甲"; + case "DIAMOND_LEGGINGS": + return "钻石护腿"; + case "DIAMOND_BOOTS": + return "钻石靴子"; + case "BREAD": + return "面包"; + case "COOKED_BEEF": + return "牛排"; + case "COOKED_PORKCHOP": + return "熟猪排"; + case "COOKED_CHICKEN": + return "熟鸡肉"; + case "GOLDEN_APPLE": + return "金苹果"; + case "ENDER_PEARL": + return "末影珍珠"; + case "BLAZE_ROD": + return "烈焰棒"; + case "GHAST_TEAR": + return "恶魂之泪"; + case "NETHER_STAR": + return "下界之星"; + case "SHULKER_SHELL": + return "潜影壳"; + case "ELYTRA": + return "鞘翅"; + case "TOTEM_OF_UNDYING": + return "不死图腾"; + case "TRIDENT": + return "三叉戟"; + case "ENCHANTED_GOLDEN_APPLE": + return "附魔金苹果"; + default: + // 转中文名格式:SANDSTONE -> 沙石 + return name.toLowerCase() + .replace("_", " "); + } + } + + // ===== Events ===== + @EventHandler + public void onJoin(PlayerJoinEvent e) { + + Player p = e.getPlayer(); + String name = p.getName(); + String ip = getPlayerIP(p); + // 强制重发资源包(清掉客户端拒绝记录) + Bukkit.getScheduler() + .runTaskLater(this, () -> { + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + }, 20L); + // 重置聊天输入状态(防止上次退出时残留) + chatInput.reset(p); + if (needsPasswordChange.contains(name)) + needsPasswordChange.remove(name); +// 改完了直接打包,要打sdf1一起 + lastActivity.put(p.getUniqueId(), + System.currentTimeMillis()); + if (needsPasswordChange.contains(name)) + needsPasswordChange.remove(name); + + boolean isBedrock = + verification.isBedrockPlayer(p); + boolean isOnlineMode = + verification.isOnlineMode(); + + if (!db.userExists(name)) { + if (config.maxAccountsPerIP > 0 + && ip != null + && !ipGroup.canRegister(ip)) { + p.kickPlayer( + buildIPKickMessage(p, ip)); + return; + } + } + if (isFrozen(p)) { + p.setAllowFlight(true); + } + + if (!"manual".equals(config.approvalMode) + && !db.userExists(name)) { + AccountRequestManager.Request req = + accountRequest.createRequest( + name, name, ip); + if (req != null) { + p.kickPlayer( + config.msg("need_approval") + .replace("{id}", + String.valueOf( + req.id))); + return; + } + } + // [ADDED] 资源包(延迟发送,不强制) + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + if (isOnlineMode) { + autoLogin(p, "premium"); + return; + } + if (isBedrock) { + autoLogin(p, "bedrock"); + return; + } + if (canAutoLogin(p)) { + autoLogin(p, "ip_reconnect"); + return; + } + afk.remove(p.getUniqueId()); + afk.recordAction(p.getUniqueId()); + hideInventoryDelayed(p, 0); + // [ADDED] 发送资源包 + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + + // [ADDED] 发送资源包(延迟5秒确保客户端就绪) + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + + + verification.verifyPremiumAsync(p, + isPremium -> { + if (isPremium) { + Bukkit.getScheduler() + .runTask(this, + () -> autoLogin(p, + "premium")); + } else { + Bukkit.getScheduler() + .runTask(this, () -> { + joinTime.put( + p.getUniqueId(), + System.currentTimeMillis()); + if (!db.userExists(name)) + p.sendMessage(config.msg( + "not_registered")); + else + p.sendMessage(config.msg( + "not_logged_in")); + }); + } + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e) { + Player p = e.getPlayer(); + String name = p.getName(); + chatInput.reset(p); + + boolean wasLoggedIn = + loggedIn.contains(name); + + // 判断是否需要保存: + // 1. 已登录 → 保存当前背包 + // 2. 未登录但有真实物品 → 保存 + // (插件安装前的老玩家) + // 3. 未登录且背包被插件清空 → 不保存 + // (避免覆盖有效备份) + boolean hasRealItems = false; + for (ItemStack it : p.getInventory() + .getContents()) { + if (it != null + && it.getType() + != Material.AIR + && !isMenuSnowball(it)) { + hasRealItems = true; + break; + } + } + + if (wasLoggedIn || hasRealItems) { + try { + String contents = + InventorySerializer + .toBase64( + p.getInventory() + .getContents()); + String armor = + InventorySerializer + .toBase64( + p.getInventory() + .getArmorContents()); + String extra = + InventorySerializer + .toBase64( + p.getInventory() + .getExtraContents()); + int level = p.getLevel(); + double exp = p.getExp(); + + debugPrintBase64( + "退出保存", name, + contents); + db.saveInventoryBackup(name, + contents, armor, extra, + level, exp); + } catch (Exception ex) { + getLogger().severe( + "[Sdf1_login] " + + "退出保存背包失败: " + + name); + } + } else { + getLogger().info( + "[BACKUP_DEBUG] 退出跳过 " + + name + + ": 背包为空" + + "(插件已清空)"); + } + + if (wasLoggedIn) { + loggedIn.remove(name); + db.setLoggedIn(name, false); + recordIPLogin(p); + } + + lastActivity.remove(p.getUniqueId()); + joinTime.remove(p.getUniqueId()); + joinLoc.remove(p.getUniqueId()); + needsPasswordChange.remove(name); + // [ADDED] 清除 Radio 熔断状态 + // [ADDED] 清除 Radio 熔断状态 + if (radioDL != null) + radioDL.onPlayerQuit( + p.getUniqueId()); + pendingAdminAuth.remove(name); + } + + private static final String MENU_SNOWBALL_TAG = + "sdf1_menu"; + + public void giveMenuSnowball(Player p) { + Object val = db.getField( + p.getName(), "menu_snowball"); + if (val != null) { + int on = val instanceof Number + ? ((Number) val).intValue() : 1; + if (on == 0) return; + } + + for (ItemStack it : p.getInventory() + .getContents()) { + if (isMenuSnowball(it)) return; + if (isCustomMenuTrigger(it)) return; + } + + ItemStack customIcon = + menuIconMgr.getIcon(p); + if (customIcon != null) { + tagAsMenuTrigger(customIcon); + // 加外观标识 + ItemMeta cm = customIcon.getItemMeta(); + if (cm != null) { + String originalName = + cm.getDisplayName(); + if (originalName == null + || originalName.isEmpty() + || originalName.equals( + customIcon.getType() + .name())) { + originalName = "自定义物品"; + } + cm.setDisplayName( + "\u00a7e\u00a7l[菜单] \u00a7f" + + originalName); + List cmLore = + cm.hasLore() + ? new ArrayList<>(cm.getLore()) + : new ArrayList<>(); + // 避免重复添加 + if (cmLore.isEmpty() + || !cmLore.get(0).contains( + "\u00a77右键打开主菜单")) { + cmLore.add(0, + "\u00a77右键打开主菜单"); + } + cm.setLore(cmLore); + customIcon.setItemMeta(cm); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem( + slot, customIcon); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), customIcon); + } + return; + } + + ItemStack snow = new ItemStack( + Material.SNOWBALL); + ItemMeta im = snow.getItemMeta(); + if (im != null) { + im.setDisplayName( + "\u00a7e\u00a7l[菜单] \u00a7f右键打开主菜单"); + List lore = new ArrayList<>(); + lore.add("\u00a77右键点击打开功能菜单"); + lore.add("\u00a78" + MENU_SNOWBALL_TAG); + im.setLore(lore); + snow.setItemMeta(im); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem(slot, snow); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), snow); + } + } + + + /** + * 手动发放雪球菜单(绕过开关检查) + */ + public void forceGiveMenuSnowball( + Player p) { + // 只检查是否已有 + for (ItemStack it : p.getInventory() + .getContents()) { + if (isMenuSnowball(it)) { + return; + } + } + ItemStack snow = new ItemStack( + Material.SNOWBALL); + ItemMeta im = snow.getItemMeta(); + if (im != null) { + im.setDisplayName( + "§e§l[菜单] §f右键打开主菜单"); + List lore = + new ArrayList<>(); + lore.add("§7右键点击打开功能菜单"); + lore.add("§8" + MENU_SNOWBALL_TAG); + im.setLore(lore); + snow.setItemMeta(im); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem( + slot, snow); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), snow); + } + } + + private boolean isMenuSnowball(ItemStack item) { + if (item == null + || item.getType() + != Material.SNOWBALL) + return false; + if (!item.hasItemMeta()) + return false; + ItemMeta im = item.getItemMeta(); + if (im == null) return false; + if (!im.hasLore()) return false; + List lore = im.getLore(); + if (lore == null) return false; + for (String line : lore) { + if (line.contains(MENU_SNOWBALL_TAG)) + return true; + } + return false; + } + // 运行时初始化版本(避免静态加载问题) + private NamespacedKey getTriggerKey() { + return new NamespacedKey(this, "menu_trigger"); + } + + /** + * 给物品打上菜单触发标记 + */ + private void tagAsMenuTrigger(ItemStack item) { + if (item == null) return; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return; + meta.getPersistentDataContainer().set( + getTriggerKey(), + PersistentDataType.STRING, + "true"); + item.setItemMeta(meta); + } + + /** + * 检查物品是否带菜单触发标记 + */ + private boolean isCustomMenuTrigger( + ItemStack item) { + if (item == null) return false; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + return meta.getPersistentDataContainer() + .has(getTriggerKey(), + PersistentDataType.STRING); + } + + + @EventHandler + public void onMove(PlayerMoveEvent e) { + Player p = e.getPlayer(); + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + // 冻结处理(未登录玩家不能移动) + if (isFrozen(p)) { + if (from.getBlockX() == to.getBlockX() + && from.getBlockY() == to.getBlockY() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + e.setCancelled(true); + p.setAllowFlight(true); + } + } + + private List filterTab(List opts, String prefix) { + List r = new ArrayList<>(); + for (String o : opts) { + if (o.toLowerCase().startsWith(prefix.toLowerCase())) { + r.add(o); + } + } + return r; + } + + + /* + 雪球菜单: + 同时检测左右手 + */ + @EventHandler + public void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + ItemStack hand = p.getInventory() + .getItemInMainHand(); + ItemStack offhand = p.getInventory() + .getItemInOffHand(); + + if (isMenuSnowball(hand) + || isMenuSnowball(offhand)) { + e.setCancelled(true); + gui.openMain(p); + return; + } + + if (isCustomMenuTrigger(hand) + || isCustomMenuTrigger(offhand)) { + if (db.hasMenuIcon(p.getName())) { + e.setCancelled(true); + gui.openMain(p); + } + } + } + + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + afk.recordAction(p.getUniqueId()); + // [ADDED] 任务计数 + if (questTracker != null) + questTracker.onBlockMined(p.getName(), + e.getBlock().getType().name()); + + } + + @EventHandler + public void onBlockPlace2(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) return; + afk.recordAction(p.getUniqueId()); + if (questTracker != null) + questTracker.onBlockPlaced( + p.getName(), + e.getBlock().getType().name()); + } + + @EventHandler + public void onChatTrack(AsyncPlayerChatEvent e) { + if (isFrozen(e.getPlayer())) return; + if (questTracker != null) + questTracker.onPlayerChat( + e.getPlayer().getName()); + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + if (isFrozen(e.getPlayer())) + e.setCancelled(true); + } + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + if (isFrozen(e.getPlayer())) { + e.setCancelled(true); + return; + } + ItemStack drop = + e.getItemDrop().getItemStack(); + if (isMenuSnowball(drop)) { + e.setCancelled(true); + } + } + + + @EventHandler + public void onPickup(PlayerPickupItemEvent e) { + if (isFrozen(e.getPlayer())) + e.setCancelled(true); + } + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (e.getEntity() instanceof Player) { + if (isFrozen((Player) e.getEntity())) + e.setCancelled(true); + } + } + + @EventHandler + public void onEntityAttack( + EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player + && isFrozen( + (Player) e.getDamager())) + e.setCancelled(true); + if (e.getEntity() instanceof Player + && isFrozen( + (Player) e.getEntity())) + e.setCancelled(true); + } + + // [ADDED] 击杀追踪 + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + if (questTracker == null) return; + Player killer = e.getEntity().getKiller(); + if (killer == null || isFrozen(killer)) return; + if (e.getEntity() instanceof Player) + questTracker.onPlayerKill(killer.getName()); + else + questTracker.onMobKill(killer.getName(), + e.getEntityType().name()); + } + + // [ADDED] 合成追踪 + @EventHandler + public void onCraft(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player) { + Player p = (Player) e.getWhoClicked(); + if (!isFrozen(p) && questTracker != null) + questTracker.onItemCrafted(p.getName()); + } + } + + // [ADDED] 钓鱼追踪 + @EventHandler + public void onFish(PlayerFishEvent e) { + Player p = e.getPlayer(); + if (p == null || isFrozen(p)) return; + if (questTracker != null + && e.getState() == PlayerFishEvent.State.CAUGHT_FISH) + questTracker.onFishCaught(p.getName()); + } + + // [ADDED] 村民交易追踪 + @EventHandler(priority = EventPriority.MONITOR, + ignoreCancelled = true) + public void onVillagerTrade(InventoryClickEvent e) { + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p)) return; + if (questTracker == null) return; + + Inventory topInv = + e.getView().getTopInventory(); + if (topInv == null + || topInv.getHolder() == null) + return; + if (!(topInv.getHolder() + instanceof org.bukkit.entity.Villager)) + return; + + // 结果槽 = 2 + if (e.getRawSlot() != 2) return; + ItemStack result = topInv.getItem(2); + if (result == null + || result.getType() == Material.AIR) + return; + + final Player fp = p; + Bukkit.getScheduler().runTaskLater(this, + () -> { + if (fp.isOnline()) + questTracker.onVillagerTrade( + fp.getName()); + }, 2L); + } + + + + @EventHandler + public void onChat(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + String msg = e.getMessage(); + // ★ CDK/经济/债券输入拦截 — 必须在所有聊天处理之前 + if (this.getCDK() != null && this.getCDK().isListening(p.getName())) { + e.setCancelled(true); + this.getCDK().onChat(p, msg); + return; + } + + +// ===== 菜单聊天输入 ===== + if (getMenu().isEditing(p.getName())) { + e.setCancelled(true); + getMenu().onChat(p, msg); + return; + } + + // ===== 冻结检查 ===== + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + // ===== 聊天输入流程(改密码/绑邮箱等) ===== + if (chatInput.isInFlow(p)) { + e.setCancelled(true); + chatInput.handleInput(p, msg, this); + return; + } + + // ===== 管理员密码验证 ===== + if (pendingAdminAuth.contains(p.getName())) { + e.setCancelled(true); + pendingAdminAuth.remove(p.getName()); + if (msg.equals(config.adminPassword)) { + p.sendMessage("§a验证通过"); + p.sendMessage("§7输入的密码已验证"); + } else { + p.sendMessage("§c密码错误"); + } + return; + } + + // ===== 聊天过滤 - 开关检查 ===== + if (!chatFilter.isEnabled()) return; + + // ===== 白名单玩家 ===== + if (chatFilter.isPlayerWhitelisted( + p.getName())) return; + + // ===== 禁言检查 ===== + if (chatFilter.isMuted(p.getName())) { + e.setCancelled(true); + p.sendMessage(chatFilter.msg( + "chat_muted")); + return; + } + + // ===== URL检测 ===== + List urls = + chatFilter.extractUrls(msg); + if (urls.isEmpty()) return; + + // 过滤白名单 + List blocked = new ArrayList<>(); + for (String url : urls) { + if (!chatFilter.isWhitelisted(url)) { + blocked.add(url); + } + } + if (blocked.isEmpty()) return; + + // 取消消息 + e.setCancelled(true); + + for (String url : blocked) { + p.sendMessage(chatFilter.msg( + "chat_url_blocked", + "url", url)); + } + + // 增加违规计数 + chatFilter.incrementViolation(p.getName()); + int count = chatFilter.getViolationCount( + p.getName()); + p.sendMessage(chatFilter.msg( + "chat_url_violation", + "count", String.valueOf(count))); + + // 通知管理员 + if (chatFilter.isNotifyAdmin()) { + String notify = chatFilter.msg( + "chat_url_admin_notify", + "player", p.getName(), + "url", blocked.get(0), + "count", String.valueOf(count)); + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.hasPermission( + "sdf1.admin") + || op.isOp()) { + op.sendMessage(notify); + } + } + } + + // 通报 + if (chatFilter.isNotifyAll()) { + Bukkit.broadcastMessage( + chatFilter.msg( + "chat_url_broadcast", + "player", + p.getName())); + } + + // 处罚 + chatFilter.applyPunishment(p, count); + // ===== 补签确认 ===== + if (pendingBackCheck.containsKey( + p.getName())) { + e.setCancelled(true); + String data = pendingBackCheck + .remove(p.getName()); + String[] parts = data.split(":"); + String dateStr = parts[0]; + int totalPoints = + Integer.parseInt(parts[1]); + + if (msg.trim().equals("1") + || msg.trim() + .equalsIgnoreCase("yes") + || msg.trim().contains("是") + || msg.trim().contains("愿意")) { + int extra = totalPoints - 10; + if (extra > 0 + && !db.deductPoints( + p.getName(), extra)) { + db.addPoints(p.getName(), 10); + p.sendMessage("§c积分也不足," + + "10积分已退还"); + return; + } + checkIn.doBackCheckIn(p, dateStr); + p.sendMessage("§a补签成功!日期: " + + dateStr + " §7(扣除" + + totalPoints + + "积分,免经济)"); + playSuccessSound(p); + } else { + db.addPoints(p.getName(), 10); + p.sendMessage("§c补签已取消," + + "10积分已退还"); + } + return; + } + + } + + // 在 getter 区域添加 + public MenuManager getMenu() { + return menu; + } + public PVPManager getPVPManager() { + return pvpManager; + } + // 在 Main.java 中(与其他 getter 放一起) + public AreaProtection getAreaProtection() { + return areaProtection; + } + +// ==================== 我的钱包 ==================== + + public void openMyWallet(Player p) { + openWalletGUI(p, p.getName()); + } + + private void openWalletGUI(Player p, String target) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l我的钱包"); + + + ItemStack glass = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta gm = glass.getItemMeta(); + if (gm != null) { + gm.setDisplayName(" "); + glass.setItemMeta(gm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, glass); + + // 债券余额 (slot 4) + int balance = bondManager.getBonds(target); + g.setItem(4, mkItem(Material.GOLD_INGOT, + "§6§l债券余额: §e" + balance + " §6枚", + "§7当前持有的债券数量")); + + // 账户状态 (slot 10) + String status = bondManager + .getAccountStatus(target); + if ("frozen".equals(status)) { + g.setItem(10, mkItem(Material.REDSTONE, + "§c§l账户状态: 冻结中", + "§7无法进行任何资金交易")); + } else { + g.setItem(10, mkItem(Material.EMERALD, + "§a§l账户状态: 正常", + "§7账户运行正常")); + } + + // 同IP玩家 (slot 11) + if (target.equals(p.getName())) { + List sameIP = bondManager + .getSameIPPlayers(target); + List lore = new ArrayList<>(); + lore.add("§7同IP下注册的其他玩家:"); + if (sameIP.isEmpty()) { + lore.add("§a 无"); + } else { + for (String n : sameIP) + lore.add("§c - " + n); + } + lore.add("§7"); + lore.add("§7转账限制:"); + lore.add("§7 不能与同IP玩家转账"); + g.setItem(11, mkItem(Material.PAPER, + "§e§l同IP玩家", lore.toArray( + new String[0]))); + } + + // 近7天流水 (slot 12) + g.setItem(12, mkItem(Material.BOOK, + "§e近7天流水", + "§7点击查看最近7天交易记录")); + + // 近14天流水 (slot 13) + g.setItem(13, mkItem(Material.BOOK, + "§e近14天流水", + "§7点击查看最近14天交易记录", + "§8(系统最大支持14天)")); + + // 转账记录 (slot 14) + g.setItem(14, mkItem(Material.PAPER, + "§e转账记录", + "§7点击查看所有转账明细")); + + // 返回 (slot 22) + g.setItem(22, mkItem(Material.ARROW, + "§7← 返回")); + + walletViewTarget.put(p.getName(), target); + p.openInventory(g); + } + +// ==================== 流水记录GUI ==================== + + public void openWalletTransactions( + Player p, int days) { + String target = walletViewTarget.getOrDefault( + p.getName(), p.getName()); + openWalletTxGUI(p, target, days, false); + } + + public void openWalletTransfers(Player p) { + String target = walletViewTarget.getOrDefault( + p.getName(), p.getName()); + openWalletTxGUI(p, target, 14, true); + } + + private void openWalletTxGUI(Player p, + String target, + int days, + boolean transfersOnly) { + days = Math.min(days, 14); // 普通玩家最大14天 + String title = transfersOnly + ? "§e§l转账记录 - " + target + : "§e§l流水记录 - 近" + days + + "天 - " + target; + + Inventory g = Bukkit.createInventory( + null, 54, title); + + List> txs = transfersOnly + ? bondManager.getTransferTransactions( + target, days) + : bondManager.getTransactions( + target, days); + + int slot = 0; + for (Map tx : txs) { + if (slot >= 45) break; + + String type = (String) tx.get("type"); + int amount = ((Number) tx.get("amount")) + .intValue(); + String tgt = (String) tx.get("target_player"); + String reason = (String) tx.get("reason"); + int bal = ((Number) tx.get("balance_after")) + .intValue(); + long time = ((Number) tx.get("time")) + .longValue(); + + boolean isExpense = "shop_buy".equals(type) + || "shop_packing".equals(type); + boolean pos = isExpense ? false : (amount > 0); + String sign = pos ? "+" : (isExpense ? "-" : ""); + String color = pos ? "§a" : (isExpense ? "§c" : (amount < 0 ? "§c" : "")); +// 金额取绝对值显示 + int displayAmount = Math.abs(amount); + + + // 用 if-else 替代 switch 避免编译问题 + Material mat = Material.PAPER; + if (BondManager.TX_TRANSFER_OUT.equals(type)) { + mat = Material.RED_WOOL; + } else if (BondManager.TX_TRANSFER_IN + .equals(type)) { + mat = Material.LIME_WOOL; + } else if (BondManager.TX_ADMIN_GIVE + .equals(type)) { + mat = Material.GOLD_BLOCK; + } else if (BondManager.TX_ADMIN_DEDUCT + .equals(type)) { + mat = Material.REDSTONE_BLOCK; + } else if (BondManager.TX_REDEEM + .equals(type)) { + mat = Material.NAME_TAG; + } else if (BondManager.TX_SHOP_BUY + .equals(type)) { + mat = Material.CHEST; + } else if (BondManager.TX_FREEZE + .equals(type)) { + mat = Material.BARRIER; + } else if (BondManager.TX_UNFREEZE + .equals(type)) { + mat = Material.EMERALD_BLOCK; + } else if (pos) { + mat = Material.LIME_WOOL; + } else { + mat = Material.RED_WOOL; + } + String typeName = walletFormatType(type); + + List lore = new ArrayList<>(); + lore.add("§7类型: " + typeName); + lore.add("§7金额: " + color + sign + displayAmount + + " §6枚"); + + if (tgt != null && !tgt.isEmpty()) + lore.add("§7对象: §e" + tgt); + if (reason != null && !reason.isEmpty()) + lore.add("§7理由: §7" + reason); + lore.add("§7余额: §f" + bal + " §6枚"); + lore.add("§8" + TX_SDF.format( + new Date(time))); + + ItemStack item = new ItemStack(mat); + ItemMeta im = item.getItemMeta(); + if (im != null) { + im.setDisplayName(color + sign + displayAmount + + " §6枚 - " + typeName); + + im.setLore(lore); + item.setItemMeta(im); + } + g.setItem(slot, item); + slot++; + } + + if (txs.isEmpty()) { + ItemStack empty = new ItemStack( + Material.BARRIER); + ItemMeta em = empty.getItemMeta(); + if (em != null) { + em.setDisplayName("§7暂无记录"); + empty.setItemMeta(em); + } + g.setItem(22, empty); + } else if (txs.size() > 45) { + g.setItem(49, mkItem(Material.PAPER, + "§7还有 " + (txs.size() - 45) + + " 条未显示")); + } + + g.setItem(53, mkItem(Material.ARROW, + "§7← 返回钱包")); + + p.openInventory(g); + } + + + private String walletFormatType(String type) { + if (type == null) return "未知"; + switch (type) { + case BondManager.TX_ADMIN_GIVE: + return "管理员发放"; + case BondManager.TX_ADMIN_DEDUCT: + return "管理员扣除"; + case BondManager.TX_TRANSFER_OUT: + return "转出"; + case BondManager.TX_TRANSFER_IN: + return "转入"; + case BondManager.TX_REDEEM: + return "CDK兑换"; + case BondManager.TX_SHOP_BUY: + return "商城购买"; + case BondManager.TX_DAILY_SIGN: + return "签到奖励"; + case BondManager.TX_FREEZE: + return "冻结"; + case BondManager.TX_UNFREEZE: + return "解冻"; + default: + return type; + } + } + + + // ===== Inventory Click(旧逻辑 + 新主菜单布局 + 新功能) ===== + @EventHandler + public void onInvClick(InventoryClickEvent e) { + if (e.isCancelled()) return; + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + String title = e.getView().getTitle(); + int slot = e.getRawSlot(); + + // ===== 工单 ===== + if (title.equals(TicketManager.T_TICKET_MAIN) + || title.equals( + TicketManager.T_TICKET_CREATE) + || title.equals( + TicketManager.T_TICKET_MY) + || title.equals( + TicketManager.T_TICKET_ADMIN) + || title.equals( + TicketManager.T_TICKET_DETAIL) + || title.equals("§d§l服务商面板") + || title.equals("§6§l抢单大厅") + || title.equals( + "§b§l我处理中的工单")) { + if (e.getClickedInventory() == null) + return; + if (e.getClickedInventory() + != e.getView().getTopInventory()) + return; + e.setCancelled(true); + ticket.handleClick(p, title, slot); + return; + } + + // ===== 垃圾箱 ===== + // ===== 垃圾回收站 ===== + if (title.equals("§6§l垃圾回收站")) { + if (e.getClickedInventory() == null) + return; + + // 玩家背包普通点击 → 完全不管 + if (e.getClickedInventory() + == p.getInventory() + && !e.isShiftClick()) { + return; + } + + // Shift+Click 从玩家背包存入 + if (e.isShiftClick() + && e.getClickedInventory() + == p.getInventory() + && e.getCurrentItem() != null + && e.getCurrentItem().getType() + != Material.AIR + && !isMenuSnowball( + e.getCurrentItem())) { + e.setCancelled(true); + long now = System.currentTimeMillis(); + Long last = garbageBusy.get( + p.getUniqueId()); + if (last != null && now - last < 200L) + return; + garbageBusy.put(p.getUniqueId(), now); + ItemStack toSave = + e.getCurrentItem().clone(); + p.getInventory() + .setItem(e.getSlot(), null); + garbage.saveItem(toSave); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage(p, 0); + }, 1L); + return; + } + + // GUI 内点击 → 全部取消,由 garbage.handleClick 处理 + e.setCancelled(true); + + // 功能格 49-53 + if (slot >= 49 && slot <= 53) { + garbage.handleClick(p, title, slot); + return; + } + + // 光标有物品 → 存入 + if (slot < 54 + && e.getCursor() != null + && e.getCursor().getType() + != Material.AIR + && !isMenuSnowball( + e.getCursor())) { + ItemStack toSave = + e.getCursor().clone(); + p.getOpenInventory().setCursor(null); + garbage.saveItem(toSave); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage(p, 0); + }, 2L); + return; + } + + // 点击有 ID 的物品 → 取出 + if (slot < 54) { + ItemStack inSlot = + e.getInventory().getItem(slot); + if (inSlot != null + && inSlot.hasItemMeta() + && inSlot.getItemMeta() + .hasLore()) { + List lore = + inSlot.getItemMeta().getLore(); + if (lore != null && !lore.isEmpty() + && lore.get(0).startsWith( + "§7ID: #")) { + try { + String numStr = lore.get(0) + .replace( + "§7ID: #", "") + .trim(); + int dbId = + Integer.parseInt( + numStr); + if (garbage.removeItem( + dbId)) { + ItemStack give = + inSlot.clone(); + ItemMeta gm = + give.getItemMeta(); + if (gm == null) + gm = Bukkit + .getItemFactory() + .getItemMeta( + give + .getType()); + gm.setLore(null); + give.setItemMeta(gm); + p.getInventory() + .addItem(give); + e.getInventory() + .setItem( + slot, null); + } + } catch (Exception ex) { + p.sendMessage( + "§c取出失败"); + } + } + } + } + return; + } + + + // ===== 二级菜单(.txt)—— 移到前面 ===== + if (title.startsWith("§6§l") + && !title.equals(GUIManager.T_MAIN) + && !title.equals(GUIManager.T_ADMIN) + && !title.equals(GUIManager.T_MY_INFO) + && !title.equals(GUIManager.T_INVITE) + && !title.equals( + GUIManager.T_TASK_CENTER) + && !title.equals( + GUIManager.T_GIFT_STAGES) + && !title.equals("§6§l任务面板") + && !title.equals( + "§6§l垃圾回收站")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + // ★ bounds检查 ★ + if (slot < 45 + && slot < e.getInventory() + .getSize() + && e.getCurrentItem() != null) { + ItemMeta subLm = + e.getCurrentItem().getItemMeta(); + if (subLm != null + && subLm.getLore() != null) { + for (String subLn + : subLm.getLore()) { + if (subLn.contains( + "§7指令: §f")) { + String cmd = + subLn.replace( + "§7指令: §f", + "").trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals( + "null")) { + p.sendMessage( + "§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) + cmd = cmd.substring(1); + Bukkit.dispatchCommand( + p, cmd); + return; + } + } + } + } + return; + } + + // ===== [DEBUG] 标题追踪 ===== + { + String dt = title; + if (dt.contains("钱包") + || dt.contains("余额") + || dt.contains("流水") + || dt.contains("转账")) { + getLogger().info( + "[DEBUG-CLICK] " + + "player=" + p.getName() + + " title=[" + dt + "]" + + " slot=" + slot + + " codePoints=" + + debugCodePoints(dt)); + } + } + + // ===== 二级菜单(.txt)—— 排除所有固定GUI ===== + if (title.startsWith("§6§l") + && !title.equals(GUIManager.T_MAIN) + && !title.equals(GUIManager.T_ADMIN) + && !title.equals(GUIManager.T_MY_INFO) + && !title.equals(GUIManager.T_INVITE) + && !title.equals( + GUIManager.T_TASK_CENTER) + && !title.equals( + GUIManager.T_GIFT_STAGES) + && !title.equals("§6§l任务面板") + && !title.equals("§6§l垃圾回收站") + && !title.equals("§6§l我的钱包") + && !title.equals("§6§l余额操作")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot < 45 + && slot < e.getInventory() + .getSize() + && e.getCurrentItem() != null) { + ItemMeta subLm = e.getCurrentItem() + .getItemMeta(); + if (subLm != null + && subLm.getLore() != null) { + for (String subLn + : subLm.getLore()) { + if (subLn.contains( + "§7指令: §f")) { + String cmd = subLn + .replace("§7指令: §f", "") + .trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals("null")) { + p.sendMessage("§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) { + cmd = cmd.substring(1); + } + Bukkit.dispatchCommand(p, cmd); + return; + } + } + } + } + return; + } + + // ===== 我的钱包(唯一handler) ===== + if (title.equals("§6§l我的钱包")) { + e.setCancelled(true); + if (slot == 22) { + gui.openMain(p); + } else if (slot == 12) { + openWalletTransactions(p, 7); + } else if (slot == 13) { + openWalletTransactions(p, 14); + } else if (slot == 14) { + openWalletTransfers(p); + } + return; + } + + // ===== 流水/转账记录(唯一handler) ===== + if (title.startsWith("§e§l流水记录") + || title.startsWith("§e§l转账记录")) { + e.setCancelled(true); + if (slot == 53) { + openMyWallet(p); + } + return; + } + + // ===== 余额操作(唯一handler) ===== + if (title.equals("§d§l余额操作")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot >= 0 + && slot < e.getInventory().getSize()) { + ItemStack item = e.getInventory() + .getItem(slot); + if (item == null + || !item.hasItemMeta() + || !item.getItemMeta() + .hasDisplayName()) { + return; + } + // 在 slot == 49 的 return 之后、读取 item 之前 + // ItemStack item = e.getInventory().getItem(slot); + if (item == null) return; + Material mat = item.getType(); +// 以下是常见的装饰性玻璃板材质,按需扩展 + if (mat.name().contains("STAINED_GLASS") + || mat.name().contains("GLASS_PANE") + || mat == Material.LIGHT_BLUE_STAINED_GLASS_PANE + || mat == Material.GRAY_STAINED_GLASS_PANE + // ... 其他装饰物 + || mat == Material.BLACK_STAINED_GLASS_PANE) { + e.setCancelled(true); + return; // 装饰物品,不处理 + } + + String targetName = item.getItemMeta() + .getDisplayName() + .replace("§e", "") + .replace("§a", ""); + if (targetName.equals(p.getName())) { + p.sendMessage("§c不能操作自己"); + return; + } + if (e.isLeftClick()) { + balanceTarget = targetName; + balanceGiveMode = true; + p.closeInventory(); + p.sendMessage( + "§e请输入金额和理由"); + p.sendMessage( + "§7例: +100 给款 / -100 扣款"); + } else if (e.isRightClick()) { + if (this.getCDK() != null) { + this.getCDK().requestInput( + p, "bond", + targetName); + } + p.closeInventory(); + p.sendMessage( + "§e【债券操作】对 §a" + + targetName + + " §e进行债券操作"); + p.sendMessage( + "§e输入 +金额 给债券," + + " -金额 扣债券"); + p.sendMessage( + "§7可附加理由: §a+100 " + + "活动奖励 §7不填默认:" + + "管理员手动调整"); + } + } + return; + } + + + + +// ===== 菜单管理 ===== + if (title.equals("§c§l菜单管理")) { + e.setCancelled(true); + if (slot == 26) { + gui.openMain(p); + } else if (slot == 22) { + gui.openEditor(p, -1); + } else if (slot >= 0 && slot < 9) { + List items = + getMenu().getItems(); + if (slot < items.size()) { + if (e.isLeftClick()) { + gui.openEditor(p, slot); + } else if (e.isRightClick()) { + MenuManager.MenuItem mi = + items.get(slot); + if ("OP".equals(mi.permType)) { + mi.permType = + "玩家"; + } else { + mi.permType = "OP"; + } + gui.openMenuManager(p); + p.sendMessage( + "§a" + mi.title + + " §7权限切换为: §e" + + mi.permType); + } + } + } + return; + } + + // ===== 全局 slot 检查(放后面) ===== + if (e.getClickedInventory() == null) return; + if (e.getClickedInventory() + != e.getView().getTopInventory()) + return; + if (slot < 0 || slot >= e.getInventory() + .getSize()) return; + if (e.getCurrentItem() == null) return; + + + // ==================== 群系传送(新) ==================== + if (title.equals(GUIManager.T_BIOME)) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot < 12) { + String[] cats = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沼泽", "河流", + "地下", "蘑菇岛", "下界", "末地" + }; + p.closeInventory(); + gui.teleportToBiome(p, cats[slot]); + } + return; + } + + // ===== 编辑器 ===== + if (title.equals(GUIManager.T_EDITOR)) { + e.setCancelled(true); + if (slot >= 27) return; + List el = + getMenu().getItems(); + int ei = el.size() - 1; + if (ei < 0) return; + MenuManager.MenuItem em = el.get(ei); + switch (slot) { + case 10: + getMenu().editTitle(p, ei); + break; + case 11: + getMenu().editCommand(p, ei); + break; + case 20: + em.permType = + "玩家".equals(em.permType) + ? "OP" : "玩家"; + gui.openEditor(p, ei); + break; + // 编辑器 case 22(保存) + case 22: + if (getMenu() != null) { + MenuManager.MenuItem last = + getMenu().getItems().get(ei); + if (last.command == null + || last.command.trim().isEmpty() + || last.command.trim() + .equals("/")) { + p.sendMessage( + "§c指令不能为空,请先填写指令"); + gui.openEditor(p, ei); + return; + } + getMenu().saveMenu(); + p.closeInventory(); + p.sendMessage("§a已保存!"); + gui.openMain(p); + } + break; + + case 24: + getMenu().loadMenu(); + // ★ 如果最后一项是空指令的新项,删掉 ★ + List cancelItems = + getMenu().getItems(); + if (!cancelItems.isEmpty()) { + MenuManager.MenuItem last = + cancelItems + .get(cancelItems.size() - 1); + if ((last.command == null + || last.command.trim().isEmpty() + || last.command.trim().equals("/")) + && "新菜单项" + .equals(last.title)) { + cancelItems.remove( + cancelItems.size() - 1); + } + } + p.closeInventory(); + p.sendMessage("§c已取消"); + gui.openMain(p); + break; + } + } + // ==================== 主菜单(新布局) ==================== + // ==================== 主菜单(新布局) ==================== + if (title.equals(GUIManager.T_MAIN)) { + e.setCancelled(true); + + // ★ 统一:所有自定义菜单项通过 lore 识别 ★ + if (e.getCurrentItem() != null) { + ItemMeta meta = e.getCurrentItem() + .getItemMeta(); + if (meta != null && meta.getLore() != null) { + for (String ln : meta.getLore()) { + if (ln.contains("§7指令: §f")) { + String cmd = ln.replace( + "§7指令: §f", "").trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals("null")) { + p.sendMessage("§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) { + cmd = cmd.substring(1); + } + if (cmd.endsWith(".txt")) { + gui.openSubMenu(p, + cmd.substring(1)); + } else { + Bukkit.dispatchCommand(p, cmd); + } + return; + } + } + } + } + + // 固定按钮 + if (slot == 10) { + gui.openMyInfo(p); + } else if (slot == 12) { + gui.openInvite(p); + } else if (slot == 14) { + p.openInventory(points.createShopGUI(p)); + } else if (slot == 16) { + gui.openBiomeMenu(p); + } else if (slot == 19) { + ticket.openMain(p); + } else if (slot == 20) { + openMyWallet(p); + } else if (slot == 21) { + dailySignWithReward(p); + gui.openMain(p); + } else if (slot == 23) { + gui.openTaskCenter(p); + } else if (slot == 25) { + if (p.isOp() || isAdmin(p)) { + garbage.openRecycle(p); + } + } else if (slot == 29) { + if (isAdmin(p)) { + p.closeInventory(); + chatInput.getState(p).type = + ChatInputManager.InputType.ADMIN_AUTH; + p.sendMessage("请输入管理密码:"); + } + } else if (slot == 31) { + if (p.isOp()) { + gui.openMenuManager(p); + } + } else if (slot == 34) { + if (p.isOp() || isAdmin(p)) { + gui.openBalanceOps(p); + } + } + + + return; + } + + // ==================== 新人礼包 ==================== + if (title.equals(GUIManager.T_GIFT_STAGES)) { + e.setCancelled(true); + if (slot == 26) { + gui.openTaskCenter(p); + } else if (slot >= 10 && slot <= 18) { + QuestTracker qt = getQuestTracker(); + if (qt != null) { + List quests = + qt.getQuests("新人任务"); + int idx = slot - 10; + if (idx >= 0 && idx < quests.size()) { + QuestTracker.QuestFile qf = + quests.get(idx); + String pn = p.getName(); + if (qt.hasClaimed(pn, qf)) { + p.sendMessage( + "§c该阶段奖励已领取"); + } else if (!qt.isStageCompleted( + pn, qf)) { + p.sendMessage("§c条件未满足"); + } else { + qt.claimRewards(p, qf); + qt.markClaimed(pn, qf); + gui.openGiftStages(p); + playSuccessSound(p); + } + } + } else { + int stage = slot - 9; + if (gift.canClaim(p, stage)) { + gift.claimReward(p, stage); + gui.openGiftStages(p); + } else { + p.sendMessage(config.msg( + "gift_not_ready")); + } + } + } + return; + } + + // ==================== 主线/支线任务 ==================== + if (title.equals("§d§l主线任务") + || title.equals("§d§l支线任务")) { + e.setCancelled(true); + if (slot == e.getInventory().getSize() - 1) { + gui.openTaskCenter(p); + } else if (questTracker != null) { + String cat = title.contains("主线") + ? "主线任务" : "支线任务"; + List quests = + questTracker.getQuests(cat); + if (slot < quests.size()) { + QuestTracker.QuestFile qf = + quests.get(slot); + if (!questTracker.isStageCompleted( + p.getName(), qf)) { + p.sendMessage("§c条件未满足"); + } else if (questTracker.hasClaimed( + p.getName(), qf)) { + p.sendMessage("§c已领取过"); + } else { + questTracker.claimRewards(p, qf); + questTracker.markClaimed( + p.getName(), qf); + gui.openTaskList(p, cat); + playSuccessSound(p); + } + } + } + return; + } + + // ==================== 我的信息 ==================== + if (title.equals(GUIManager.T_MY_INFO)) { + e.setCancelled(true); + // ★ 只在图标槽(slot 4)调用,不干扰按钮 + if (slot == 4 + && gui.handleMyInfoIconClick(p, e)) { + return; + } + if (slot == 26) gui.openMain(p); + + else if (slot == 10) { + if (checkIn.isCheckedInToday( + p.getName())) + p.sendMessage( + config.msg("checkin_already")); + else { + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn( + p.getName()); + playSuccessSound(p); + gui.openMyInfo(p); + } + } else if (slot == 11) { + chatInput.getState(p).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP1; + p.closeInventory(); + p.sendMessage("§e请输入当前密码:"); + } else if (slot == 12) { + chatInput.getState(p).type = + ChatInputManager.InputType + .BACK_CHECKIN; + p.closeInventory(); + p.sendMessage("§e请输入补签日期:"); + } else if (slot == 13) { + String code = (String) db.getField( + p.getName(), "invite_code"); + if (code == null || code.isEmpty()) + code = invite.generateCode(p); + p.sendMessage("§e邀请码: " + code); + } else if (slot == 14) { + chatInput.getState(p).type = + ChatInputManager.InputType + .SET_EMAIL; + p.closeInventory(); + p.sendMessage("§e请输入邮箱:"); + } else if (slot == 15) { + p.openInventory( + points.createShopGUI(p)); + } + return; + } + + + // ==================== 管理面板 ==================== + if (title.equals(GUIManager.T_ADMIN)) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) { + chatInput.getState(p).type = + ChatInputManager.InputType + .SMTP_CONFIG; + p.closeInventory(); + p.sendMessage("§e当前SMTP: " + + config.getSmtp("smtp地址")); + p.sendMessage( + "§e输入新地址(0跳过):"); + } else if (slot == 11) + gui.openUserManagement(p); + else if (slot == 12) gui.openMyInfo(p); + else if (slot == 14) + p.openInventory( + points.createShopGUI(p)); + else if (slot == 16) { + if (e.getClick().isLeftClick()) { + config.afkEnabled = + !config.afkEnabled; + p.sendMessage(config.msg( + config.afkEnabled + ? "afk_set_enabled" + : "afk_set_disabled")); + config.saveSettings(); + gui.openAdmin(p); + } else if (e.getClick() + .isRightClick()) { + p.closeInventory(); + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_AFK_TIME; + p.sendMessage( + "§e输入挂机时长(分钟):"); + } + } + return; + } + + // ==================== 任务中心 ==================== + if (title.equals(GUIManager.T_TASK_CENTER)) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) + gui.openGiftStages(p); + else if (slot == 12) { + if (questTracker != null) { + Location loc = p.getLocation(); + String result = + questTracker + .checkInAtPosition( + p.getName(), + p.getWorld() + .getName(), + loc.getX(), + loc.getY(), + loc.getZ()); + p.sendMessage(result); + playSuccessSound(p); + } + } else if (slot == 14) + gui.openTaskList(p, "主线任务"); + else if (slot == 16) + gui.openTaskList(p, "支线任务"); + else if (slot == 18) + ticket.openMain(p); + else if (slot == 20) { + dailySignWithReward(p); + gui.openMain(p); + } + return; + } + + // ==================== 邀请 ==================== + if (title.equals(GUIManager.T_INVITE)) { + e.setCancelled(true); + if (slot == 26) gui.openMain(p); + else if (slot == 10) { + String code = + invite.generateCode(p); + p.sendMessage("§e邀请码: " + code); + gui.openInvite(p); + } else if (slot == 16) { + chatInput.getState(p).type = + ChatInputManager.InputType + .INVITE_INPUT_CODE; + p.closeInventory(); + p.sendMessage("§e请输入邀请码:"); + } + return; + } + + // ==================== 用户管理 ==================== + if (title.equals(GUIManager.T_USER_MGMT)) { + e.setCancelled(true); + if (slot == 53) gui.openAdmin(p); + else if (slot >= 0 && slot < 45) { + ItemStack item = + e.getInventory().getItem(slot); + if (item != null && item.hasItemMeta() + && item.getItemMeta() + .hasDisplayName()) + gui.openUserDetail(p, + item.getItemMeta() + .getDisplayName() + .replace("§e", "") + .replace("§a", "")); + } + return; + } + + // ==================== 用户详情 ==================== + if (title.startsWith("§e§l管理: ")) { + e.setCancelled(true); + String tgt = title.substring( + "§e§l管理: ".length()); + if (slot == 22) gui.openUserManagement(p); + else if (slot == 10) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_POINTS; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage( + "§e输入" + tgt + "的新积分:"); + } else if (slot == 12) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_PWD; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage( + "§e输入" + tgt + "的临时密码:"); + } else if (slot == 14) { + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) { + forceGiveMenuSnowball(tp); + p.sendMessage( + "§a已发放雪球菜单给 " + + tgt); + } else { + p.sendMessage("§c玩家不在线"); + } + } else if (slot == 16) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_DELETE_CONFIRM; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage(config.msg( + "admin_delete_confirm")); + } + return; + } + + // ==================== 积分商城 ==================== + if (title.startsWith("§d§l积分商城")) { + e.setCancelled(true); + if (slot == 49) gui.openMain(p); + else if (slot == 50 && isAdmin(p)) + gui.openShopAdmin(p); + else points.handleClick(p, slot); + return; + } + + // ==================== 商城管理 ==================== + if (title.equals("§c§l商城管理")) { + e.setCancelled(true); + if (slot == 49) + p.openInventory( + points.createShopGUI(p)); + return; + } + + // ==================== CY背包商城 ==================== + if (title.startsWith("§d§lCY背包商城")) { + e.setCancelled(true); + int size = e.getInventory().getSize(); + if (slot == size - 1) + p.openInventory( + points.createShopGUI(p)); + else points.handleCYClick(p, slot); + return; + } + + // ==================== 任务面板 ==================== + if (title.equals("§6§l任务面板")) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 11) { + Location loc = p.getLocation(); + String result = + questTracker.checkInAtPosition( + p.getName(), + p.getWorld().getName(), + loc.getX(), loc.getY(), + loc.getZ()); + p.sendMessage(result); + playSuccessSound(p); + openTaskPanel(p); + } else if (slot == 15) { + gui.openGiftStages(p); + } + return; + } + + // ==================== 账号请求面板 ==================== + if (title.equals("§e§l账号请求")) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) { + p.closeInventory(); + p.sendMessage("§c§l[确认] §f请输入:"); + p.sendMessage("§e/sdf1_login del " + + p.getName()); + } else if (slot == 13) { + ticket.openCreate(p); + } else if (slot == 16) { + p.closeInventory(); + loginMgr.handleReset(p); + } + } + } + + /* + * 签到 + 盲盒经济奖励 + */ + public void dailySignWithReward(Player p) { + String name = p.getName(); + + if (checkIn.isCheckedInToday(name)) { + p.sendMessage( + config.msg("checkin_already")); + return; + } + + // ===== 1. 执行签到(内部已加积分) ===== + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn(name); + + // ===== 2. 获取连续天数 ===== + int streak = checkIn.getStreak(name); + double multiplier = 1.0; + if (streak >= 21) { + multiplier = 1.3; + } else if (streak >= 14) { + multiplier = 1.2; + } else if (streak >= 7) { + multiplier = 1.1; + } + + // ===== 3. 经济奖励(不再重复加积分) ===== + if (economy == null) { + playSuccessSound(p); + return; + } + + // 固定金额模式 + if ("fixed".equals( + config.checkinRewardType) + && config.checkinRewardFixed > 0) { + final double reward = + config.checkinRewardFixed; + final double m = multiplier; + p.sendMessage("§e§l[签到] §f" + + "正在生成今日奖励..."); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + double rewardFinal = + Math.round( + reward * m + * 100.0) + / 100.0; + double before = + economy + .getBalance(p); + economy.depositPlayer( + p, rewardFinal); + double after = + economy + .getBalance(p); + p.sendMessage( + "§6§l[签到] §f" + + "获得奖励: §a§l$" + + String.format( + "%.2f", + rewardFinal)); + p.sendMessage("§7余额: §e" + + String.format( + "%.2f", before) + + " §7→ §a" + + String.format( + "%.2f", after)); + playSuccessSound(p); + }, 30L); + return; + } + + // 盲盒范围模式 + if ("range".equals( + config.checkinRewardType) + && config.checkinRewardMin > 0 + && config.checkinRewardMax + > config.checkinRewardMin) { + final double min = + config.checkinRewardMin; + final double max = + config.checkinRewardMax; + final double m = multiplier; + getLogger().info( + "[签到] 盲盒: min=" + + min + " max=" + max + + " multi=" + m); + p.sendMessage("§e§l[签到盲盒] " + + "§f正在生成今日惊喜..."); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + double rand = + Math.random(); + double reward = min + + rand + * (max - min); + reward = Math.round( + reward * m * 100.0) + / 100.0; + getLogger().info( + "[签到] 盲盒结果: rand=" + + String.format( + "%.4f", rand) + + " reward=" + + reward); + double before = + economy + .getBalance(p); + economy.depositPlayer( + p, reward); + double after = + economy + .getBalance(p); + p.sendMessage( + "§6§l====== 签到盲盒 ======"); + p.sendMessage( + "§7开出: §a§l$" + + String.format( + "%.2f", + reward)); + p.sendMessage("§7余额: §e" + + String.format( + "%.2f", before) + + " §7→ §a" + + String.format( + "%.2f", after)); + p.sendMessage( + "§6§l========================"); + playSuccessSound(p); + }, 40L); + return; + } + + // 无经济奖励 + playSuccessSound(p); + } + + public BondManager getBondManager() { + return bondManager; + } + + + @EventHandler + public void onVillagerTrade( + org.bukkit.event.inventory.TradeSelectEvent e) { + // TradeSelectEvent 在玩家点击交易时触发 + if (!(e.getWhoClicked() instanceof Player)) return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p) || questTracker == null) return; + questTracker.onVillagerTrade(p.getName()); + } + + @EventHandler + public void onInvDrag(InventoryDragEvent e) { + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + String t = e.getView().getTitle(); +// 禁止T_MY_INFO拖拽,只允许点击操作 + // 我的信息:允许拖入图标槽(31) + if (GUIManager.T_MY_INFO.equals( + e.getView().getTitle())) { + boolean toIconSlot = false; + for (int raw : e.getRawSlots()) { + if (raw == 31) { + toIconSlot = true; + break; + } + } + if (toIconSlot) { + ItemStack dragged = + e.getOldCursor().clone(); + String b64 = gui.serializeItem(dragged); + if (b64 != null) { + db.saveMenuIcon(p.getName(), b64, + dragged.getType().name()); + } + p.sendMessage("§a已保存菜单图标: " + + dragged.getType().name()); + // 刷新GUI显示 + Bukkit.getScheduler() + .runTaskLater(this, () -> + gui.openMyInfo(p), 2L); + } + e.setCancelled(true); + return; + } + + + // 回收站拖拽 + if (t.equals("§6§l垃圾回收站")) { + boolean toGUI = false; + for (int rawSlot : e.getRawSlots()) { + if (rawSlot < 54) { + toGUI = true; + break; + } + } + if (toGUI) { + ItemStack cur = e.getCursor(); + if (cur != null + && cur.getType() != Material.AIR + && !isMenuSnowball(cur)) { + e.setCancelled(true); + p.getOpenInventory().setCursor(null); + garbage.saveItem(cur.clone()); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage( + p, + garbage + .getRecyclePage( + p + .getName())); + }, 2L); + return; + } + } + return; + } + + + + //服务商工单系统 + if (t.equals(TicketManager.T_TICKET_MAIN) + || t.equals( + TicketManager.T_TICKET_CREATE) + || t.equals( + TicketManager.T_TICKET_MY) + || t.equals( + TicketManager.T_TICKET_ADMIN) + || t.equals( + TicketManager.T_TICKET_DETAIL) + || t.equals("§d§l服务商面板") + || t.equals("§6§l抢单大厅") + || t.equals( + "§b§l我处理中的工单") + || t.equals("§6§l我的历史工单") + || t.equals("§c§l商城管理")) { + e.setCancelled(true); + } + } + /** 验证玩家是否在主库中真实存在 */ + public boolean isRealPlayer(String name) { + if (name == null || name.isEmpty()) return false; + try { + Map user = + getDb().getUser(name); + return user != null && !user.isEmpty() + && user.containsKey("player_name"); + } catch (Exception e) { + return false; + } + } + + // ===== Commands ===== + @Override + public boolean onCommand(CommandSender sender, + Command cmd, String label, + String[] args) { + + String cmdName = cmd.getName().toLowerCase(); + // 区域防护(必须在最前面) + if (cmd.getName().equalsIgnoreCase("protect") + || cmd.getName().equals("区域保护")) { + if (areaProtection == null) { + sender.sendMessage("§c区域防护未初始化"); + return true; + } + if (args.length == 0) { + sender.sendMessage("§a§l===== 区域防护 ====="); + sender.sendMessage("§e/protect 工具 §7获取选区工具"); + sender.sendMessage("§e/protect 创建 <区域名> §7创建区域"); + sender.sendMessage("§e/protect 列表 §7查看所有区域"); + sender.sendMessage("§e/protect 重载 §7重载配置"); + sender.sendMessage("§e/protect add <玩家> §7全局加白"); + sender.sendMessage("§e/protect add <区域> <玩家> §7区域加白"); + sender.sendMessage("§e/protect remove <玩家> §7全局删白"); + sender.sendMessage("§e/protect remove <区域> <玩家> §7区域删白"); + sender.sendMessage("§e/protect additem <物品ID> §7全局加黑"); + sender.sendMessage("§e/protect additem <区域> <物品ID> §7区域加黑"); + sender.sendMessage("§e/protect removeitem <物品ID> §7全局删黑"); + sender.sendMessage("§e/protect removeitem <区域> <物品ID> §7区域删黑"); + sender.sendMessage("§e/protect list §7全局白名单"); + sender.sendMessage("§e/protect list <区域> §7区域白名单"); + sender.sendMessage("§e/protect listitem §7全局物品黑名单"); + sender.sendMessage("§e/protect listitem <区域> §7区域物品黑名单"); + sender.sendMessage("§e/protect expand [格数] §7扩建选区"); + sender.sendMessage("§e/protect contraction [格数] §7收缩选区"); + sender.sendMessage("§e/protect addname <区域> <名字> §7添加和平白名单生物"); + sender.sendMessage("§e/protect removename <区域> <名字> §7移除和平白名单生物"); + sender.sendMessage("§e/protect addwhite <玩家> §7添加模式排除玩家"); + sender.sendMessage("§e/protect removewhite <玩家> §7移除模式排除玩家"); + sender.sendMessage("§e/protect listname <区域> §7查看和平白名单"); + sender.sendMessage("§e/protect listwhite §7查看模式排除名单"); + + return true; + } + // 其他子命令交给 areaProtection + try { + return areaProtection.handleCommand(sender, args); + } catch (Exception e) { + sender.sendMessage("§c执行出错: " + e.getMessage()); + e.printStackTrace(); + return true; + } + } + + + if (cmd.getName().equalsIgnoreCase("shop") + || cmd.getName().equals("商店")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可使用"); + return true; + } + if (shopManager != null) { + shopManager.openShopMain((Player) sender); + } else { + sender.sendMessage("§c商店系统未加载"); + } + return true; + } + + String sub = (args.length > 0) ? args[0].toLowerCase() : ""; + +// ===== 在 onCommand 方法体内,最开头加: ===== + if (cmd.getName().equalsIgnoreCase("sdf1_login") + && args.length > 0 + && args[0].equalsIgnoreCase("shop")) { + if (shopManager == null) { + sender.sendMessage("§c商店未加载"); + return true; + } + return shopManager.handleCommand(sender, args); + } + // set + if (sub.equals("set")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage("§e用法: /sdf1_login set <玩家> <临时密码>"); + return true; + } + String tgt = args[1]; + String tempPwd = args[2]; + if (!db.userExists(tgt)) { + sender.sendMessage("§c玩家不存在"); + return true; + } + String salt = (String) db.getField(tgt, "password_salt"); + String hash = PasswordUtils.hash(tempPwd, salt); + db.setField(tgt, "temp_password", hash); + db.setField(tgt, "temp_pw_expire", + System.currentTimeMillis() + 300000L); + db.setField(tgt, "temp_pw_used", 0); + + // ★ 立即发邮件(踢人之前) + String emailAddr = (String) db.getField(tgt, "email"); + if (emailAddr != null && !emailAddr.isEmpty()) { + // 获取最近登录信息 + Object lastLoginObj = db.getField(tgt, "last_login_time"); + long lastLogin = lastLoginObj != null + ? ((Number) lastLoginObj).longValue() : 0; + String lastIP = (String) db.getField(tgt, "last_ip"); + if (lastIP == null || lastIP.isEmpty()) + lastIP = "未知"; + + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String loginTimeStr = lastLogin > 0 + ? sdf.format(new java.util.Date(lastLogin)) + : "未知时间"; + + final String to = emailAddr; + final String fName = tgt; + final String pwd = tempPwd; + final String ipInfo = lastIP; + final String timeInfo = loginTimeStr; + final Main self = this; + + Bukkit.getScheduler().runTaskAsynchronously(this, + () -> { + // ★ 发送临时密码邮件(带登录信息) + boolean sent = email.sendTempPassword( + to, fName, pwd); + // ★ 额外发送安全通知邮件 + if (sent) { + String body = "玩家 " + fName + + ":\n\n" + + "管理员已为您的账号设置了临时密码。\n\n" + + "临时密码: " + pwd + "\n" + + "过期时间: 5分钟内\n\n" + + "最近登录记录:\n" + + " 时间: " + timeInfo + "\n" + + " IP地址: " + ipInfo + "\n\n" + + "如果您本人未操作,请忽略此邮件。\n" + + "如果您怀疑账号被盗,请立即修改密码。"; + email.sendBody(to, + "【安全通知】账号临时密码已设置", body); + } + Bukkit.getScheduler().runTask(self, () -> { + if (sent) { + sender.sendMessage("§a临时密码已发送至 " + + tgt + " 的邮箱"); + } + }); + }); + } else { + sender.sendMessage("§7该玩家未绑定邮箱,跳过邮件通知"); + } + + // 踢人 + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + tp.kickPlayer("§c管理员已设置了临时密码,请用新密码登录"); + sender.sendMessage("§a已为 " + tgt + " 设置临时密码"); + return true; + } + + + if (label.equalsIgnoreCase("pvp")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + return pvpManager.onCommand(p, args); + } + + if (cmdName.equals("cypay")) { + if (args.length > 0) { + String target = args[0]; + if (!getDb().userExists(target)) { + sender.sendMessage("§c玩家 " + target + + " 不存在于服务器"); + return true; + } + } + return cypayCommand.onCommand( + sender, cmd, label, args); + } + + + + // ===== /printer [玩家名] ===== + if (label.equalsIgnoreCase("printer")) { + if (bondPrinter == null) { + sender.sendMessage("§cPrinter未初始化"); + return true; + } + if (!sender.isOp() + && !isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length == 0) { + // 打印全服 + sender.sendMessage( + "§e正在导出全服流水..."); + bondPrinter.printAll(sender); + return true; + } + if (args.length == 1) { + // 打印指定玩家 + String target = args[0]; + sender.sendMessage("§e正在导出 " + + target + " 的流水..."); + bondPrinter.printPlayer( + target, sender); + return true; + } + sender.sendMessage( + "§7用法: /printer [玩家名]"); + return true; + } + // ===== /chat 独立命令 ===== + if (cmdName.equals("chat") + || cmdName.equals("聊天")) { + if (sender instanceof Player) { + Player chatP = (Player) sender; + if (!chatP.getScoreboardTags() + .contains(config.adminTag)) { + sender.sendMessage("§c权限不足"); + return true; + } + } + if (args.length < 1) { + sender.sendMessage( + "§e/chat "); + return true; + } + String cs = args[0].toLowerCase(); + if (cs.equals("reload")) { + chatFilter.loadConfig(); + sender.sendMessage("§a聊天配置已重载"); + return true; + } + if (cs.equals("开") || cs.equals("on")) { + chatFilter.setEnabled(true); + chatFilter.saveConfig(); + sender.sendMessage("§aURL过滤已开启"); + return true; + } + if (cs.equals("关") || cs.equals("off")) { + chatFilter.setEnabled(false); + chatFilter.saveConfig(); + sender.sendMessage("§cURL过滤已关闭"); + return true; + } + if (cs.equals("add")) { + if (args.length < 2) { + sender.sendMessage("§e/chat add "); + return true; + } + chatFilter.addUrl(args[1]); + sender.sendMessage("§a已添加白名单: " + args[1]); + return true; + } + if (cs.equals("remove")) { + if (args.length < 2) { + sender.sendMessage("§e/chat remove "); + return true; + } + if (chatFilter.removeUrl(args[1])) + sender.sendMessage("§c已移除: " + args[1]); + else + sender.sendMessage("§c未找到: " + args[1]); + return true; + } + if (cs.equals("addplayer")) { + if (args.length < 2) { + sender.sendMessage("§e/chat addplayer <玩家>"); + return true; + } + chatFilter.addWhitelistPlayer(args[1]); + sender.sendMessage("§a白名单玩家: " + args[1]); + return true; + } + if (cs.equals("takeplayer")) { + if (args.length < 2) { + sender.sendMessage("§e/chat takeplayer <玩家>"); + return true; + } + if (chatFilter.removeWhitelistPlayer(args[1])) + sender.sendMessage("§c已移除白名单: " + args[1]); + else + sender.sendMessage("§c未找到: " + args[1]); + return true; + } + if (cs.equals("unmute")) { + if (args.length < 2) { + sender.sendMessage("§e/chat unmute <玩家>"); + return true; + } + chatFilter.unmutePlayer(args[1]); + sender.sendMessage("§a已解禁: " + args[1]); + return true; + } + if (cs.equals("reset")) { + if (args.length < 2) { + sender.sendMessage("§e/chat reset <玩家>"); + return true; + } + chatFilter.resetPlayer(args[1]); + sender.sendMessage("§a已重置: " + args[1]); + return true; + } + sender.sendMessage("§c未知子命令"); + return true; + } + if (label.equalsIgnoreCase("sdf1_login") + && args.length >= 2 + && args[0].equalsIgnoreCase("give")) { + if (!sender.isOp()) { + sender.sendMessage("§c仅OP可用"); + return true; + } + Player target = + Bukkit.getPlayer(args[1]); + if (target == null || !target.isOnline()) { + sender.sendMessage( + "§c玩家不在线: " + args[1]); + return true; + } + giveMenuSnowball(target); + sender.sendMessage("§a已发放菜单雪球给 " + + target.getName()); + target.sendMessage( + "§a管理员已发放菜单雪球给你"); + return true; + } + + if (cmdName.equals("recycle")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player pr = (Player) sender; + if (!pr.isOp() + && !pr.getScoreboardTags() + .contains(config.adminTag)) { + pr.sendMessage("§c权限不足"); + return true; + } + garbage.openRecycle(pr); + return true; + } + + + // /垃圾清理 + if (cmdName.equals("垃圾清理")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + int count = garbage.collectItems(); + sender.sendMessage("§a已清理 " + + count + " 个掉落物"); + return true; + } + // /menu 开关雪球菜单 + if (cmdName.equals("menu")) { + if (!(sender instanceof Player)) { + sender.sendMessage( + "§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length == 0) { + p2.sendMessage( + "§e用法: /menu <开/关>"); + return true; + } + String arg = args[0]; + if (arg.equals("开")) { + db.setField(p2.getName(), + "menu_snowball", 1); + p2.sendMessage( + "§a雪球菜单已开启," + + "下次进入将发放"); + } else if (arg.equals("关")) { + db.setField(p2.getName(), + "menu_snowball", 0); + p2.sendMessage( + "§c雪球菜单已关闭," + + "下次进入不再发放"); + } else { + p2.sendMessage( + "§e用法: /menu <开/关>"); + } + return true; + } + + if (cmdName.equals("reg") + || cmdName.equals("注册")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + chatInput.reset(p); + if (db.userExists(p.getName())) { + p.sendMessage( + "§c您已注册过,请使用 /login"); + return true; + } + try { + loginMgr.handleRegister(p, args); + // [ADDED] 欢迎仪式 + if (welcome != null + && loggedIn.contains(p.getName())) { + welcome.onRegister(p); + } + + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] reg命令异常: " + + e.getMessage()); + e.printStackTrace(); + p.sendMessage("§c注册出错,请联系管理员"); + } + return true; + } + + + if (cmdName.equals("login") + || cmdName.equals("l") + || cmdName.equals("登录")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + chatInput.reset(p); + if (loggedIn.contains(p.getName())) { + p.sendMessage("§c您已登录"); + return true; + } + try { + loginMgr.handleLogin(p, args); + // [FIX] 手动登录后关闭飞行(仅一次) + if (loggedIn.contains(p.getName()) + && p.getAllowFlight()) { + p.setAllowFlight(false); + p.setFlying(false); + } + + // [ADDED] 欢迎仪式 + if (welcome != null + && loggedIn.contains(p.getName())) { + welcome.onLogin(p); + } + + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] login命令异常: " + + e.getMessage()); + e.printStackTrace(); + p.sendMessage("§c登录出错,请联系管理员"); + } + return true; + } + + // /找回密码 + if (cmdName.equals("找回密码")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + handleReset((Player) sender); + return true; + } + + // /签到 + if (cmdName.equals("签到")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (dailySign((Player) sender)) + playSuccessSound((Player) sender); + else + ((Player) sender).sendMessage( + config.msg("checkin_already")); + return true; + } + + // 绑定邮箱 + if (cmdName.equals("绑定邮箱")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 1) { + String emailAddr = args[0]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null + && !oldEmail.isEmpty()) { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + + oldEmail); + p2.sendMessage( + "§e请输入登录密码以确认修改:"); + } else { + String code = String.valueOf( + (int) (Math.random() + * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = email + .sendVerifyCode(to, c); + Bukkit.getScheduler().runTask( + this, () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + + + // /oa + if (cmdName.equals("oa")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + openAccountRequestPanel( + (Player) sender); + return true; + } + + // /玩家信息 + if (cmdName.equals("玩家信息")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + gui.openMain((Player) sender); + return true; + } + + // ===== /sdf1_login ===== + if (!cmdName.equals("sdf1_login")) + return false; + + if (args.length == 0) { + if (!(sender instanceof Player)) { + sender.sendMessage( + "§e用法: /sdf1_login <子命令>"); + return true; + } + gui.openMain((Player) sender); + return true; + } + +// ===== del / confirm / cancel(最先处理) ===== + if (sub.equals("del")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login del <玩家>"); + return true; + } + String target = args[1]; + if (!db.userExists(target)) { + sender.sendMessage( + "§c玩家 " + target + " 不存在"); + return true; + } + if (pendingDeleteTask != null) { + pendingDeleteTask.cancel(); + String old = pendingDeleteName; + pendingDeleteTask = null; + pendingDeleteName = null; + getLogger().info( + "[Sdf1_login] 取消旧待删除: " + old); + } + pendingDeleteName = target; + pendingDeleteTask = new BukkitRunnable() { + @Override + public void run() { + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + if (name == null) return; + db.deleteUser(name); + getLogger().info( + "[Sdf1_login] 已删除: " + name); + Player tp = Bukkit.getPlayer(name); + if (tp != null && tp.isOnline()) + tp.kickPlayer( + "§c账号已被管理员删除"); + } + }; + pendingDeleteTask.runTaskLater(this, 1200L); + sender.sendMessage("§e§l[删除确认] 目标: §c" + + target); + sender.sendMessage("§760秒后执行," + + "§a/sdf1_login confirm §7确认," + + "§c/sdf1_login cancel §7取消"); + getLogger().warning("[Sdf1_login] 待删除: " + + target + " by " + sender.getName()); + return true; + } + + if (sub.equals("confirm")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (pendingDeleteName == null + || pendingDeleteTask == null) { + sender.sendMessage( + "§c当前没有待删除的账号"); + return true; + } + pendingDeleteTask.cancel(); + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + db.deleteUser(name); + sender.sendMessage("§a已立即删除: " + name); + Player tp = Bukkit.getPlayer(name); + if (tp != null && tp.isOnline()) + tp.kickPlayer("§c账号已被管理员删除"); + return true; + } + + if (sub.equals("cancel")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (pendingDeleteName == null + || pendingDeleteTask == null) { + sender.sendMessage( + "§c当前没有待删除的账号"); + return true; + } + pendingDeleteTask.cancel(); + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + sender.sendMessage("§a已取消删除: " + name); + return true; + } + +// ===== stop ===== + if (sub.equals("stop")) { + if (!(sender instanceof Player)) { + if (args.length >= 2) { + db.clearTempPassword(args[1]); + sender.sendMessage("§a已报废 " + + args[1] + " 的临时密码"); + } else { + db.clearAllTempPasswords(); + sender.sendMessage("§a已报废所有临时密码"); + } + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String tgt = args[1]; + String myIP = getPlayerIP(p2); + String targetIP = + db.getPlayerIPByName(tgt); + if (targetIP == null) { + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + targetIP = getPlayerIP(tp); + } + if (targetIP == null + || !targetIP.equals(myIP)) { + p2.sendMessage("§c只能操作同IP下的玩家"); + return true; + } + db.clearTempPassword(tgt); + p2.sendMessage("§a已报废 " + tgt + " 的临时密码"); + } else { + db.clearTempPassword(p2.getName()); + p2.sendMessage("§a已报废您的临时密码"); + } + return true; + } + +// ===== reload ===== + if (sub.equals("reload")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + config.loadMessages(); + config.loadSmtp(); + config.loadSettings(); + chatFilter.loadConfig(); + if (radio != null) radio.reload(); + if (getMenu() != null) { + getMenu().loadMenu(); + } + if (shopManager != null) { + shopManager.loadCategories(); + } + if (shopManager != null) shopManager.loadCategories(); + if (areaProtection != null) { + areaProtection.clearAllPlayerAreas(); + } + if (questTracker != null) { + questTracker.reload(); + } + sender.sendMessage("§a§l配置已重载!"); + return true; + } + +// ===== back ===== + if (sub.equals("back")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + // ... 保持原有back代码不变 ... + if (args.length < 2) { + sender.sendMessage("§e用法: /sdf1_login back <玩家> [#编号]"); + return true; + } + // 原有back逻辑 + String backTarget = args[1]; + if (args.length >= 3 + && args[2].startsWith("#")) { + int backupId; + try { + backupId = Integer.parseInt( + args[2].replace("#", "")); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效编号"); + return true; + } + String[] backup = + db.getInventoryBackupById(backupId); + if (backup == null) { + sender.sendMessage("§c备份 #" + backupId + " 不存在"); + return true; + } + Player tp = Bukkit.getPlayer(backTarget); + if (tp != null && tp.isOnline()) { + try { + tp.getInventory().setContents( + InventorySerializer.fromBase64(backup[0])); + if (backup[1] != null && !backup[1].isEmpty()) + tp.getInventory().setArmorContents( + InventorySerializer.fromBase64(backup[1])); + if (backup[2] != null && !backup[2].isEmpty()) + tp.getInventory().setExtraContents( + InventorySerializer.fromBase64(backup[2])); + int lv = Integer.parseInt(backup[3]); + if (lv > 0) tp.setLevel(lv); + tp.sendMessage("§a管理员已恢复您的背包"); + sender.sendMessage("§a已恢复 " + backTarget + " (备份#" + backupId + ")"); + } catch (Exception e) { + sender.sendMessage("§c恢复失败: " + e.getMessage()); + } + } else { + db.saveInventoryBackup(backTarget, + backup[0], backup[1], backup[2], + Integer.parseInt(backup[3]), + Double.parseDouble(backup[4])); + sender.sendMessage("§a" + backTarget + " 离线,备份已写入"); + } + } else { + List> backups = + db.getInventoryBackups(backTarget, 5); + if (backups.isEmpty()) { + sender.sendMessage("§c" + backTarget + " 没有背包备份"); + return true; + } + sender.sendMessage("§e§l=== " + backTarget + " 背包备份 ==="); + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat("MM-dd HH:mm:ss"); + for (Map b : backups) { + int bid = ((Number) b.get("id")).intValue(); + Object stObj = b.get("save_time"); + long st = stObj != null + ? ((Number) stObj).longValue() : 0; + String time = st > 0 + ? sdf.format(new java.util.Date(st)) + : "未知时间"; + String itemNames = getBackupItemPreview(bid); + sender.sendMessage("§7#" + bid + " §f" + time + " §7: " + itemNames); + } + sender.sendMessage("§7使用 §e/sdf1_login back " + backTarget + " #编号 §7恢复"); + } + return true; + } + +// ===== pw / password ===== + if (sub.equals("pw") || sub.equals("password")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + + if (args.length >= 3) { + String oldPwd = args[1]; + String newPwd = args[2]; + if (!PasswordUtils.validate(newPwd)) { + p2.sendMessage(config.msg("password_format_error")); + return true; + } + String salt = (String) db.getField( + p2.getName(), "password_salt"); + String hash = PasswordUtils.hash(oldPwd, salt); + boolean mainOk = db.checkPassword( + p2.getName(), hash); + boolean tempOk = db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg("password_wrong")); + return true; + } + String oldHash = (String) db.getField( + p2.getName(), "password_hash"); + String newSalt = PasswordUtils.generateSalt(); + String newHash = PasswordUtils.hash(newPwd, newSalt); + if (newHash.equals(oldHash)) { + p2.sendMessage(config.msg("password_same")); + return true; + } + recordPasswordChange(p2.getName(), oldHash, salt); + db.setField(p2.getName(), "password_hash", newHash); + db.setField(p2.getName(), "password_salt", newSalt); + if (!mainOk && tempOk) + db.clearTempPassword(p2.getName()); + // pw 命令改密码成功后 + needsPasswordChange.remove(p2.getName()); +// 清除临时密码 + if (!mainOk && tempOk) + db.clearTempPassword(p2.getName()); + + return true; + } + + if (args.length == 2) { + String oldPwd = args[1]; + String salt = (String) db.getField( + p2.getName(), "password_salt"); + String hash = PasswordUtils.hash(oldPwd, salt); + boolean mainOk = db.checkPassword( + p2.getName(), hash); + boolean tempOk = db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg("password_wrong")); + return true; + } + chatInput.getState(p2).type = + ChatInputManager.InputType.CHANGE_PWD_STEP2; + chatInput.getState(p2).ticketTitle = + mainOk ? "main" : "temp"; + p2.sendMessage("§e请输入新密码:"); + return true; + } + + chatInput.getState(p2).type = + ChatInputManager.InputType.CHANGE_PWD_STEP1; + p2.sendMessage("§e请输入当前密码:"); + return true; + } + +// ===== email ===== + if (sub.equals("email")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String emailAddr = args[1]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null && !oldEmail.isEmpty()) { + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL_VERIFY; + chatInput.getState(p2).targetPlayer = emailAddr; + chatInput.getState(p2).ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + oldEmail); + p2.sendMessage("§e请输入登录密码以确认修改:"); + } else { + String code = String.valueOf( + (int) (Math.random() * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL_VERIFY; + chatInput.getState(p2).targetPlayer = emailAddr; + chatInput.getState(p2).ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = + email.sendVerifyCode(to, c); + Bukkit.getScheduler() + .runTask(this, () -> { + if (sent) { + fp.sendMessage("§a验证码已发送到 " + to); + fp.sendMessage("§e输入6位验证码(0取消):"); + } else { + fp.sendMessage("§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + +// ===== sign ===== + if (sub.equals("sign")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + dailySignWithReward((Player) sender); + return true; + } + +// ===== reset ===== + if (sub.equals("reset")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + loginMgr.handleReset((Player) sender); + return true; + } + +// ===== undo(密码撤销) ===== + if (sub.equals("undo")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + PwdRollback rb = pwdRollback.get(p2.getName()); + if (rb == null) { + p2.sendMessage("§c无可撤销记录"); + return true; + } + if (System.currentTimeMillis() - rb.time > 600000L) { + pwdRollback.remove(p2.getName()); + p2.sendMessage("§c撤销已过期"); + return true; + } + db.setField(p2.getName(), "password_hash", rb.hash); + db.setField(p2.getName(), "password_salt", rb.salt); + pwdRollback.remove(p2.getName()); + p2.sendMessage("§a密码已撤销!"); + return true; + } + + // radio子命令(控制台+玩家) + // sdf1_login radio → 停止全服广播 + // sdf1_login radio reload → 重载 + // sdf1_login radio <歌名> → 全服播放 + // sdf1_login radio <歌名> <玩家名> → 指定玩家播放 + // sdf1_login radio <歌名> login → 推送给登录中玩家 + if (sub.equals("radio")) { + if (args.length < 2) { + // 无参数 = 停止 + radio.stopMainRadio(); + sender.sendMessage("§c全服广播已停止"); + return true; + } + String arg1 = args[1]; + + // reload + if (arg1.equalsIgnoreCase("reload")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + radio.reload(); + sender.sendMessage("§aRadio已重载"); + return true; + } + + // radio <歌名> → 全服播放 + // radio <歌名> <玩家名> → 指定玩家 + // radio <歌名> login → 登录阶段播放 + String songName = arg1; + + if (args.length >= 3) { + String target = args[2]; + + if (target.equalsIgnoreCase("login")) { + if (radio.hasSong(songName)) { + radio.playToLoginPlayers(songName); + sender.sendMessage( + "§a已推送 §e" + songName + + " §a给登录中玩家"); + } else { + sender.sendMessage( + "§c未找到歌曲: " + songName); + return true; + } + } else { + Player targetP = + Bukkit.getPlayer(target); + if (targetP != null + && targetP.isOnline()) { + if (radio.hasSong(songName)) { + radio.playToPlayer( + targetP, songName); + sender.sendMessage( + "§a已向 §e" + target + + " §a播放 §e" + + songName); + targetP.sendMessage( + "§6§l[广播] §f正在播放: §e" + + songName); + } else { + sender.sendMessage( + "§c未找到歌曲: " + + songName); + } + } else { + sender.sendMessage( + "§c玩家不在线: " + + target); + } + return true; + } + } else { + if (radio.hasSong(songName)) { + radio.playToAll(songName); + sender.sendMessage( + "§a全服播放: §e" + songName); + } else { + sender.sendMessage( + "§c未找到歌曲: " + songName); + } + return true; + } + + + // pw / password + if (sub.equals("pw") + || sub.equals("password")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + + if (args.length >= 3) { + // /sdf1_login pw 旧密码 新密码 + String oldPwd = args[1]; + String newPwd = args[2]; + if (!PasswordUtils.validate( + newPwd)) { + p2.sendMessage(config.msg( + "password_format_error")); + return true; + } + String salt = (String) db.getField( + p2.getName(), + "password_salt"); + String hash = + PasswordUtils.hash( + oldPwd, salt); + boolean mainOk = + db.checkPassword( + p2.getName(), hash); + boolean tempOk = + db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg( + "password_wrong")); + return true; + } + String oldHash = + (String) db.getField( + p2.getName(), + "password_hash"); + String newSalt = + PasswordUtils.generateSalt(); + String newHash = + PasswordUtils.hash( + newPwd, newSalt); + if (newHash.equals(oldHash)) { + p2.sendMessage(config.msg( + "password_same")); + return true; + } + recordPasswordChange( + p2.getName(), + oldHash, salt); + db.setField(p2.getName(), + "password_hash", newHash); + db.setField(p2.getName(), + "password_salt", newSalt); + if (!mainOk && tempOk) { + db.clearTempPassword( + p2.getName()); + } + needsPasswordChange.remove( + p2.getName()); + p2.sendMessage(config.msg( + "password_changed")); + return true; + } + + if (args.length == 2) { + // /sdf1_login pw 旧密码 + // 验证旧密码,然后聊天输入新密码 + String oldPwd = args[1]; + String salt = (String) db.getField( + p2.getName(), + "password_salt"); + String hash = + PasswordUtils.hash( + oldPwd, salt); + boolean mainOk = + db.checkPassword( + p2.getName(), hash); + boolean tempOk = + db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg( + "password_wrong")); + return true; + } + chatInput.getState(p2).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP2; + chatInput.getState(p2) + .ticketTitle = + mainOk ? "main" : "temp"; + p2.sendMessage( + "§e请输入新密码:"); + return true; + } + + // /sdf1_login pw (无参数) + chatInput.getState(p2).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP1; + p2.sendMessage( + "§e请输入当前密码:"); + return true; + } + + // email + if (sub.equals("email")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String emailAddr = args[1]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null + && !oldEmail.isEmpty()) { + // 已绑定:先验证密码 + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + + oldEmail); + p2.sendMessage( + "§e请输入登录密码以确认修改:"); + } else { + // 未绑定:直接发验证码 + String code = String.valueOf( + (int) (Math.random() + * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = email + .sendVerifyCode(to, c); + Bukkit.getScheduler().runTask( + this, () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + + + // sign + if (sub.equals("sign")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (dailySign((Player) sender)) + playSuccessSound((Player) sender); + else + ((Player) sender).sendMessage( + config.msg("checkin_already")); + return true; + } + + // reset + if (sub.equals("reset")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + loginMgr.handleReset( + (Player) sender); + return true; + } + + // undo + if (sub.equals("undo")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + PwdRollback rb = + pwdRollback.get(p2.getName()); + if (rb == null) { + p2.sendMessage("§c无可撤销记录"); + return true; + } + if (System.currentTimeMillis() - rb.time + > 600000L) { + pwdRollback.remove(p2.getName()); + p2.sendMessage("§c撤销已过期"); + return true; + } + db.setField(p2.getName(), + "password_hash", rb.hash); + db.setField(p2.getName(), + "password_salt", rb.salt); + pwdRollback.remove(p2.getName()); + p2.sendMessage("§a密码已撤销!"); + return true; + } + + // take + if (sub.equals("take")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage( + "§e用法: /sdf1_login take <玩家> <积分>"); + return true; + } + int amount; + try { + amount = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + if (!db.deductPoints(args[1], amount)) { + sender.sendMessage("§c积分不足"); + return true; + } + sender.sendMessage("§a已扣除 " + args[1] + + " " + amount + " 积分"); + return true; + } + + + // give - 手动发放雪球菜单 + if (sub.equals("give")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login give <玩家名>"); + return true; + } + Player tp = Bukkit.getPlayer( + args[1]); + if (tp == null || !tp.isOnline()) { + sender.sendMessage( + "§c玩家不在线: " + + args[1]); + return true; + } + forceGiveMenuSnowball(tp); + tp.sendMessage( + "§a管理员已为您发放菜单雪球"); + sender.sendMessage( + "§a已发放雪球菜单给 " + + args[1]); + return true; + } + + // shopadd + if (sub.equals("shopadd")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 4) { + sender.sendMessage( + "§e用法: /sdf1_login shopadd <名称> <价格> <命令>"); + return true; + } + String shopName = args[1]; + int price; + try { + price = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + String shopCmd = args[3]; + points.addItem(shopName, price, shopCmd); + sender.sendMessage("§a已添加商品: " + + shopName + " (" + price + + "积分)"); + return true; + } + + // shopdel + if (sub.equals("shopdel")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login shopdel <序号>"); + return true; + } + try { + int idx = Integer.parseInt(args[1]); + points.removeItem(idx); + sender.sendMessage("§a已删除商品 #" + idx); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + } + return true; + } + + // get + if (sub.equals("get")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login get <玩家>"); + return true; + } + Map user = + db.getUser(args[1]); + if (user.isEmpty()) { + sender.sendMessage("§c玩家不存在"); + return true; + } + sender.sendMessage( + "§e=== " + args[1] + " ==="); + sender.sendMessage("§7积分: §e" + + user.getOrDefault("points", 0)); + sender.sendMessage("§7邮箱: §e" + + user.getOrDefault("email", "无")); + sender.sendMessage("§7签到天数: §e" + + user.getOrDefault( + "total_checkin_days", 0)); + sender.sendMessage("§7邀请码: §e" + + user.getOrDefault( + "invite_code", "无")); + return true; + } + + + // kick (挂机) + if (sub.equals("kick")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login kick <开关> [分钟]"); + return true; + } + String mode = args[1].toLowerCase(); + if (mode.equals("on") + || mode.equals("开") + || mode.equals("启用")) { + config.afkEnabled = true; + if (args.length >= 3) { + try { + config.afkTimeout = + Integer.parseInt( + args[2]) * 60; + } catch (NumberFormatException ignored) { + } + } + config.saveSettings(); + int minutes = + config.afkTimeout / 60; + sender.sendMessage( + "§a挂机踢出已开启,超时: " + + minutes + " 分钟"); + } else if (mode.equals("off") + || mode.equals("关") + || mode.equals("停用")) { + config.afkEnabled = false; + config.saveSettings(); + sender.sendMessage( + "§c挂机踢出已关闭"); + } else { + sender.sendMessage( + "§c无效参数 on/off"); + } + return true; + } + + // add + if (sub.equals("add")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage( + "§e用法: /sdf1_login add <玩家> <积分>"); + return true; + } + int amount; + try { + amount = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + db.addPoints(args[1], amount); + sender.sendMessage("§a已为 " + args[1] + + " 增加 " + amount + " 积分"); + return true; + } + + // ticket + if (sub.equals("ticket") + || sub.equals("工单")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + ticket.openMain((Player) sender); + return true; + } + + sender.sendMessage("§c未知参数"); + return true; + } + + return true; + } + /** + * 供SDF1反射调用的CDK兑换 + * 返回: "success:金额:余额前:余额后" + * 或 "fail:原因" + */ + public String redeemBondForExternal( + String playerName, String code) { + if (cdkManager == null) + return "fail:CDK未初始化"; + return cdkManager.redeem(code, playerName); + } + // 旧的(可能缺失或拼写错误) + public SalesStatsManager getSalesStats() { + return salesStats; + } + + /** + * 是否是垃圾站展示物品(带ID lore) + */ + private boolean isTrashDisplayItem( + ItemStack item) { + if (item == null + || item.getType() + == Material.AIR) + return false; + if (!item.hasItemMeta()) + return false; + ItemMeta im = item.getItemMeta(); + if (im == null || !im.hasLore()) + return false; + List lore = im.getLore(); + if (lore == null || lore.isEmpty()) + return false; + return lore.get(0) + .startsWith("§7ID: #"); + } + + private void sendChatHelp(Player p) { + p.sendMessage( + "§e§l======== 聊天过滤帮助 =========="); + p.sendMessage("§e/chat reload §7- 重载配置"); + p.sendMessage("§e/chat 开/关 §7- 开启/关闭聊天过滤"); + p.sendMessage("§e/chat add §7- 添加白名单URL"); + p.sendMessage("§e/chat remove §7- 移除白名单URL"); + p.sendMessage("§e/chat addplayer <玩家> §7- 添加白名单玩家"); + p.sendMessage("§e/chat takeplayer <玩家> §7- 移除白名单玩家"); + p.sendMessage("§e/chat unmute <玩家> §7- 解禁玩家"); + p.sendMessage("§e/chat reset <玩家> §7- 重置违规记录"); + p.sendMessage( + "§e§l=================================="); + } + + private int findEmptySlot(Inventory inv) { + for (int i = 0; i < 49; i++) { + ItemStack item = inv.getItem(i); + if (item == null + || item.getType() + == Material.AIR) + return i; + } + return -1; + } + + // ===== TabComplete ===== + @Override + public List onTabComplete( + CommandSender sender, Command cmd, + String label, String[] args) { + List list = new ArrayList<>(); + if (cmd.getName().equalsIgnoreCase( + "sdf1_login") + && args.length == 1) { + if (isAdmin(sender)) { + list.addAll(Arrays.asList( + "reload", "pw", "email", + "sign", "reset", "undo", + "get", "del", "set", + "take", "add", "kick", + "ticket", "oa", "stop", + "shopadd", "shopdel", "back", "radio", "back", "shop")); + } else { + list.addAll(Arrays.asList( + "pw", "email", "sign", + "reset", "undo", + "ticket", "oa", "stop")); + } + } + if (cmd.getName().equalsIgnoreCase("protect") + || cmd.getName().equals("区域保护")) { + + String sub = args.length > 0 ? args[0].toLowerCase() : ""; + + // ===== 第一层:子命令 ===== + if (args.length == 1) { + List opts = Arrays.asList( + "工具", "创建", "列表", "重载", "删除", + "add", "remove", + "additem", "removeitem", + "on", "off", "tempon", + "expand", "contraction", + "list", "listitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite"); + return filterTab(opts, args[0]); + } + + // ===== 第二层 ===== + if (args.length == 2) { + // add/remove: 区域名 + global + 在线玩家 + if (sub.equals("add") || sub.equals("remove")) { + List all = new ArrayList<>(); + all.add("global"); + all.addAll(areaProtection.getAreaNames()); + for (Player pl : Bukkit.getOnlinePlayers()) + all.add(pl.getName()); + return filterTab(all, args[1]); + } + // additem/removeitem: 区域名 + global + if (sub.equals("additem") || sub.equals("removeitem")) { + List all = new ArrayList<>(); + all.add("global"); + all.addAll(areaProtection.getAreaNames()); + return filterTab(all, args[1]); + } + // list/listitem: 区域名 + if (sub.equals("list") || sub.equals("listitem")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // expand/contraction: 数字 + if (sub.equals("expand") || sub.equals("contraction")) { + return Arrays.asList("1", "3", "5", "10", "20", "50"); + } + // 创建/删除: 区域名 + if (sub.equals("创建") || sub.equals("删除")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // addname/removename/listname: 区域名 + if (sub.equals("addname") || sub.equals("removename") + || sub.equals("listname")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // addwhite/removewhite/listwhite: 区域名 + if (sub.equals("addwhite") || sub.equals("removewhite") + || sub.equals("listwhite")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + } + + // ===== 第三层 ===== + if (args.length == 3) { + // add/remove: 第三层是玩家名 + if (sub.equals("add") || sub.equals("remove")) { + List players = new ArrayList<>(); + for (Player pl : Bukkit.getOnlinePlayers()) + players.add(pl.getName()); + return filterTab(players, args[2]); + } + // additem/removeitem: 第三层是物品ID + if (sub.equals("additem") || sub.equals("removeitem")) { + List items = Arrays.asList( + "TNT", "BROWN_MUSHROOM", "RED_MUSHROOM", + "MUSHROOM_STEW", "POISONOUS_POTATO", + "SPIDER_EYE", "POTION", "SPLASH_POTION", + "OMINOUS_BOTTLE", "ENDER_PEARL", + "BOW", "CROSSBOW", "ARROW", + "FLINT_AND_STEEL"); + return filterTab(items, args[2]); + } + // addname/removename: 第三层是生物名字(自由输入) + if (sub.equals("addname") || sub.equals("removename")) { + return null; + } + // addwhite/removewhite: 第三层是玩家名 + if (sub.equals("addwhite") || sub.equals("removewhite")) { + List players = new ArrayList<>(); + for (Player pl : Bukkit.getOnlinePlayers()) + players.add(pl.getName()); + return filterTab(players, args[2]); + } + } + + return null; + } + + + if (cmd.getName().equalsIgnoreCase("chat") + && args.length == 1 + && isAdmin(sender)) { + list.addAll(Arrays.asList( + "reload", "add", "remove", + "addplayer", "takeplayer", + "unmute", "reset")); + } + if (cmd.getName().equalsIgnoreCase("cypay")) { + return cypayCommand.onTabComplete(sender, cmd, label, args); + } + + return list; + } +} \ No newline at end of file diff --git a/backup/20260605_185500/QuestStorage.java.backup b/backup/20260605_185500/QuestStorage.java.backup new file mode 100644 index 0000000..45dbabf --- /dev/null +++ b/backup/20260605_185500/QuestStorage.java.backup @@ -0,0 +1,138 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class QuestStorage { + + private Connection conn; + + public QuestStorage(File dataFolder) { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + dataFolder, + "quest_data.db"); + conn = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getPath()); + initTable(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void initTable() + throws SQLException { + Statement stmt = + conn.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_conditions (" + + "player TEXT NOT NULL, " + + "condition_key TEXT NOT NULL, " + + "value TEXT DEFAULT '1', " + + "timestamp INTEGER, " + + "PRIMARY KEY " + + "(player, condition_key))"); + stmt.close(); + } + + /** + * 标记条件已完成(value = "1") + */ + public void setCondition( + String player, + String condition) { + setConditionRaw( + player, condition, "1"); + } + + /** + * 写入任意值(计数器等) + */ + public void setConditionRaw( + String player, + String condition, + String value) { + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO " + + "quest_conditions " + + "(player, " + + "condition_key, " + + "value, timestamp) " + + "VALUES " + + "(?, ?, ?, ?)"); + ps.setString(1, player); + ps.setString(2, condition); + ps.setString(3, value); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 查询条件值 + */ + public Object getCondition( + String player, + String condition) { + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value FROM " + + "quest_conditions " + + "WHERE player = ? " + + "AND " + + "condition_key = ?"); + ps.setString(1, player); + ps.setString(2, condition); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + String val = + rs.getString("value"); + rs.close(); + ps.close(); + // 尝试解析为数字 + try { + if (val.contains(".")) { + return Double.parseDouble( + val); + } + return Long.parseLong(val); + } catch (NumberFormatException e) { + return val; + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/backup/20260605_185500/QuestTracker.java.backup b/backup/20260605_185500/QuestTracker.java.backup new file mode 100644 index 0000000..0089ee2 --- /dev/null +++ b/backup/20260605_185500/QuestTracker.java.backup @@ -0,0 +1,3692 @@ +package Sdf1_login; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.generator.structure.Structure; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +// 如果 Bonds 在 Sdf1_login 包下: +import Sdf1_login.BondManager; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class QuestTracker { + + private final Plugin plugin; + private final QuestStorage storage; + private final Map + playerBiome = new HashMap<>(); + private final Map> + questCache = new HashMap<>(); + private final List + checkInRecords = new ArrayList<>(); + private final Map + lastCheckInPos = new HashMap<>(); + private static boolean loggedStructureRegistry + = false; + + public void recordCheckIn( + String player, + String biomeKey, + String category, + String worldName, + double x, double y, double z) { + String record = player + + "|" + worldName + + "|" + biomeKey + + "|" + category + + "|" + String.format( + "%.1f", x) + + "|" + String.format( + "%.1f", y) + + "|" + String.format( + "%.1f", z) + + "|" + System.currentTimeMillis(); + checkInRecords.add(record); + plugin.getLogger().info( + "[QuestTracker] 打卡记录: " + + record); + } + + public List getCheckInRecords() { + return new ArrayList<>(checkInRecords); + } + + public List getPlayerRecords( + String player) { + List list = new ArrayList<>(); + for (String r : checkInRecords) { + if (r.startsWith( + player.toLowerCase() + + "|")) { + list.add(r); + } + } + return list; + } + + + public QuestTracker(Plugin plugin) { + this.plugin = plugin; + this.storage = new QuestStorage( + plugin.getDataFolder()); + + loadAllQuests(); + } + + // ========== 重载任务配置 ========== + public void reload() { + questCache.clear(); + loadAllQuests(); + plugin.getLogger().info( + "[QuestTracker] 任务配置已重载"); + } + + public void debugPlayer(String player) { + plugin.getLogger().info( + "[QuestTracker] === " + + player + " 任务数据 ==="); + } + public void shutdown() { + if (storage != null) { + storage.close(); + } + } + + /** + * 玩家移动时记录当前群系 + */ + public void onPlayerMove( + String player, + String biomeName) { + playerBiome.put(player, biomeName); + } + /** + * 标记玩家已打卡该群系 + */ + // ========== 群系分类映射表 ========== + private static final Map + BIOME_CATEGORY = new HashMap<>(); + static { + // 海洋群系 + BIOME_CATEGORY.put("minecraft:ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:frozen_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_frozen_ocean", "海洋"); + // 平原群系 + BIOME_CATEGORY.put("minecraft:plains", "平原"); + BIOME_CATEGORY.put("minecraft:sunflower_plains", "平原"); + BIOME_CATEGORY.put("minecraft:meadow", "平原"); + // 森林群系 + BIOME_CATEGORY.put("minecraft:forest", "森林"); + BIOME_CATEGORY.put("minecraft:flower_forest", "森林"); + BIOME_CATEGORY.put("minecraft:birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:dark_forest", "森林"); + BIOME_CATEGORY.put("minecraft:taiga", "森林"); + BIOME_CATEGORY.put("minecraft:snowy_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_pine_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_spruce_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:cherry_grove", "森林"); + BIOME_CATEGORY.put("minecraft:jungle", "森林"); + BIOME_CATEGORY.put("minecraft:sparse_jungle", "森林"); + BIOME_CATEGORY.put("minecraft:bamboo_jungle", "森林"); + // 沙漠群系 + BIOME_CATEGORY.put("minecraft:desert", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna_plateau", "沙漠"); + BIOME_CATEGORY.put("minecraft:beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:snowy_beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:windswept_savanna", "沙漠"); + // 恶地群系 + BIOME_CATEGORY.put("minecraft:badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:wooded_badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:eroded_badlands", "恶地"); + // 雪原群系 + BIOME_CATEGORY.put("minecraft:snowy_plains", "雪原"); + BIOME_CATEGORY.put("minecraft:grove", "雪原"); + BIOME_CATEGORY.put("minecraft:ice_spikes", "雪原"); + BIOME_CATEGORY.put("minecraft:jagged_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_river", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:stony_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:snowy_slopes", "雪原"); + // 沙砾群系 + BIOME_CATEGORY.put("minecraft:windswept_hills", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_forest", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_gravelly_hills", "沙砾"); + // 沼泽群系 + BIOME_CATEGORY.put("minecraft:swamp", "沼泽"); + BIOME_CATEGORY.put("minecraft:mangrove_swamp", "沼泽"); + // 河流 + BIOME_CATEGORY.put("minecraft:river", "河流"); + // 蘑菇岛 + BIOME_CATEGORY.put("minecraft:mushroom_fields", "蘑菇岛"); + // 地下群系 + BIOME_CATEGORY.put("minecraft:dripstone_caves", "地下"); + BIOME_CATEGORY.put("minecraft:lush_caves", "地下"); + BIOME_CATEGORY.put("minecraft:deep_dark", "地下"); + // 下界群系 + BIOME_CATEGORY.put("minecraft:nether_wastes", "下界"); + BIOME_CATEGORY.put("minecraft:crimson_forest", "下界"); + BIOME_CATEGORY.put("minecraft:warped_forest", "下界"); + BIOME_CATEGORY.put("minecraft:soul_sand_valley", "下界"); + BIOME_CATEGORY.put("minecraft:basalt_deltas", "下界"); + // 岩岸/沙滩 + BIOME_CATEGORY.put("minecraft:stony_shore", + "石岸"); +// 海岸 + BIOME_CATEGORY.put("minecraft:mushroom_fields", + "蘑菇岛"); +// 苍白之园 + BIOME_CATEGORY.put( + "minecraft:pale_garden", "森林"); +// 末地小型岛屿 + BIOME_CATEGORY.put( + "minecraft:small_end_islands", "末地"); +// 主世界结构归类 + BIOME_CATEGORY.put("minecraft:village", "平原"); + BIOME_CATEGORY.put( + "minecraft:desert_pyramid", "沙漠"); + BIOME_CATEGORY.put( + "minecraft:jungle_pyramid", "森林"); + BIOME_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + + // 末地群系 + BIOME_CATEGORY.put("minecraft:the_end", "末地"); + BIOME_CATEGORY.put("minecraft:the_void", "末地"); + BIOME_CATEGORY.put("minecraft:end_highlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_midlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_barrens", "末地"); + + + } + + // 所有群系大类列表 + private static final String[] + ALL_CATEGORIES = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沙砾", "沼泽", + "河流", "石岸", "蘑菇岛", "地下", + "下界", "末地" + }; + + // ===== 结构映射(PaperMC 1.21.4 实际结构 key)===== + private static final Map + STRUCTURE_CATEGORY = + new HashMap<>(); + static { + // 主世界结构 + STRUCTURE_CATEGORY.put( + "minecraft:village_plains", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_desert", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_savanna", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_snowy", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_taiga", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:desert_pyramid", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:jungle_pyramid", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + STRUCTURE_CATEGORY.put( + "minecraft:igloo", "雪原"); + STRUCTURE_CATEGORY.put( + "minecraft:mansion", "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:pillager_outpost", + "掠夺者前哨站"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft_mesa", + "地下"); + // 要塞可生成在任何主世界群系下,映射为"主世界" + // 结构检测返回"主世界"时跳过Y轴"地下"判定 + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", + "主世界"); + // 远古城市是结构,应分类为"远古城市"而非"地下" + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "远古城市"); + STRUCTURE_CATEGORY.put( + "minecraft:trail_ruins", + "远古遗迹"); + STRUCTURE_CATEGORY.put( + "minecraft:trial_chambers", + "试炼密室"); + STRUCTURE_CATEGORY.put( + "minecraft:monument", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_cold", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_warm", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck_beached", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:buried_treasure", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_desert", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_jungle", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_mountain", + "山地"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_ocean", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_swamp", + "沼泽"); + // 下界结构 + STRUCTURE_CATEGORY.put( + "minecraft:fortress", "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:bastion_remnant", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:nether_fossil", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_nether", + "下界"); + // 末地结构 + STRUCTURE_CATEGORY.put( + "minecraft:end_city", "末地"); + } + + // 所有群系分类的条件关键词映射 + private static final Map + CATEGORY_KEYWORDS = new HashMap<>(); + static { + CATEGORY_KEYWORDS.put("海洋", "海洋"); + CATEGORY_KEYWORDS.put("平原", "平原"); + CATEGORY_KEYWORDS.put("森林", "森林"); + CATEGORY_KEYWORDS.put("沙漠", "沙漠"); + CATEGORY_KEYWORDS.put("恶地", "恶地"); + CATEGORY_KEYWORDS.put("雪原", "雪原"); + CATEGORY_KEYWORDS.put("沙砾", "沙砾"); + CATEGORY_KEYWORDS.put("沼泽", "沼泽"); + CATEGORY_KEYWORDS.put("河流", "河流"); + CATEGORY_KEYWORDS.put("蘑菇岛", "蘑菇岛"); + CATEGORY_KEYWORDS.put("地下", "地下"); + CATEGORY_KEYWORDS.put("下界", "下界"); + CATEGORY_KEYWORDS.put("末地", "末地"); + // 条件文本中的别名映射 + CATEGORY_KEYWORDS.put("地狱", "下界"); + CATEGORY_KEYWORDS.put("下届", "下界"); + CATEGORY_KEYWORDS.put("主岛", "末地"); + CATEGORY_KEYWORDS.put("石岸", "石岸"); + CATEGORY_KEYWORDS.put("沙滩", "石岸"); + CATEGORY_KEYWORDS.put("海岸", "石岸"); + + // ===== 结构名称关键词映射 ===== + // 主世界结构 + CATEGORY_KEYWORDS.put("村庄", "村庄"); + CATEGORY_KEYWORDS.put("沙漠神殿", "沙漠"); + CATEGORY_KEYWORDS.put("丛林神庙", "森林"); + CATEGORY_KEYWORDS.put("沼泽小屋", "沼泽"); + CATEGORY_KEYWORDS.put("雪屋", "雪原"); + CATEGORY_KEYWORDS.put("林地府邸", "森林"); + CATEGORY_KEYWORDS.put("掠夺者前哨站", "平原"); + CATEGORY_KEYWORDS.put("废弃矿井", "地下"); + CATEGORY_KEYWORDS.put("要塞", "主世界"); + CATEGORY_KEYWORDS.put("远古城市", "远古城市"); + CATEGORY_KEYWORDS.put("古迹废墟", "地下"); + CATEGORY_KEYWORDS.put("试炼密室", "地下"); + CATEGORY_KEYWORDS.put("海底废墟", "海洋"); + CATEGORY_KEYWORDS.put("沉船", "海洋"); + CATEGORY_KEYWORDS.put("埋藏的宝箱", "海洋"); + CATEGORY_KEYWORDS.put("废弃传送门", "主世界"); + CATEGORY_KEYWORDS.put("化石", "主世界"); + + // 下界结构 + CATEGORY_KEYWORDS.put("下界要塞", "下界"); + CATEGORY_KEYWORDS.put("猪灵堡垒", "下界"); + CATEGORY_KEYWORDS.put("玄武岩结构", "下界"); + + // 末地结构 + CATEGORY_KEYWORDS.put("末地城", "末地"); + CATEGORY_KEYWORDS.put("末地船", "末地"); + + // 下界相关别名 + CATEGORY_KEYWORDS.put("地狱要塞", "下界"); + CATEGORY_KEYWORDS.put("下界堡垒", "下界"); + + } + + /** + * 用世界维度名分类(最可靠) + * 世界名含 the_nether → 下界 + * 世界名含 the_end → 末地 + * 其余 → 主世界 + */ + public String mapBiomeCategory( + String rawKey, String worldName) { + // 优先用世界名判断维度 + if (worldName != null) { + String w = + worldName.toLowerCase(); + if (w.contains("nether")) + return "下界"; + if (w.contains("end")) + return "末地"; + } + + // 用群系映射表 + if (rawKey != null) { + String lower = + rawKey.toLowerCase(); + // 带 minecraft: 前缀的直接查 + String cat = + BIOME_CATEGORY.get(lower); + if (cat != null) + return cat; + + // 不带前缀的尝试补全 + String prefixed = + "minecraft:" + lower; + cat = BIOME_CATEGORY.get(prefixed); + if (cat != null) + return cat; + + // 模糊匹配群系名 + for (Map.Entry entry + : BIOME_CATEGORY.entrySet()) { + if (entry.getKey() + .contains(lower) + || lower.contains( + entry.getKey() + .replace( + "minecraft:", + ""))) { + return entry.getValue(); + } + } + } + + return "主世界"; + } + + // ========== 结构检测 ========== + + /** + * 检测指定位置是否存在自然生成的结构 + * 返回结构分类(如"村庄"、"沙漠"等),未找到返回null + * 使用 PaperMC World.hasStructureAt() + Registry.STRUCTURE API + */ + private String detectStructureAt( + String worldName, double x, + double y, double z) { + try { + World w = Bukkit.getWorld(worldName); + if (w == null) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测: 世界 '" + + worldName + "' 不存在"); + return null; + } + + int bx = (int) Math.floor(x); + int by = (int) Math.floor(y); + int bz = (int) Math.floor(z); + + // 首次调用时打印所有注册表结构键 + if (!loggedStructureRegistry) { + plugin.getLogger().info( + "[QuestTracker] === 结构注册表诊断 ==="); + int count = 0; + for (Structure s : Registry.STRUCTURE) { + NamespacedKey key = + Registry.STRUCTURE.getKey(s); + plugin.getLogger().info( + "[QuestTracker] 注册表结构 #" + + count + ": " + key); + count++; + } + plugin.getLogger().info( + "[QuestTracker] 注册表共 " + + count + " 个结构"); + plugin.getLogger().info( + "[QuestTracker] === 诊断结束 ==="); + loggedStructureRegistry = true; + } + + // 第1步: 精确位置检查 + io.papermc.paper.math.Position exactPos = + io.papermc.paper.math.Position.block( + bx, by, bz); + for (Structure structure : Registry.STRUCTURE) { + boolean hasAt = + w.hasStructureAt(exactPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY.get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(精确): " + + keyStr + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + + // 第2步: Y轴偏移检查 (-5 到 +10) + for (int dy = -5; dy <= 10; dy++) { + if (dy == 0) continue; + int testY = by + dy; + io.papermc.paper.math.Position testPos = + io.papermc.paper.math.Position.block( + bx, testY, bz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + testPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(Y偏移" + + dy + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + } + + // 第3步: 水平偏移检查 (±1格) + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (dx == 0 && dz == 0) continue; + io.papermc.paper.math.Position offsetPos = + io.papermc.paper.math.Position + .block(bx + dx, by, + bz + dz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + offsetPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey( + structure); + String keyStr = (key != null) + ? key.toString() + : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(偏移" + + dx + "," + + dz + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat + : "未映射")); + if (cat != null) return cat; + } + } + } + } + + plugin.getLogger().info( + "[QuestTracker] 所有位置检查完毕, " + + "未匹配任何结构 (pos=" + + bx + "," + by + "," + bz + + ")"); + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测异常: " + + ex.getClass().getSimpleName() + + ": " + ex.getMessage()); + ex.printStackTrace(); + } + return null; + } + + /** + * 将结构中文名映射到对应的大类 + * 用于匹配任务条件中的结构名 + */ + private String mapStructureCategory( + String structureName) { + if (structureName == null) return ""; + + // 主世界结构 + if (structureName.contains("村庄")) + return "村庄"; + if (structureName.contains("沙漠神殿")) + return "沙漠"; + if (structureName.contains("丛林神庙") || structureName.contains("丛林神殿")) + return "森林"; + if (structureName.contains("沼泽小屋")) + return "沼泽"; + if (structureName.contains("雪屋") || structureName.contains("冰屋")) + return "雪原"; + if (structureName.contains("林地府邸")) + return "森林"; + if (structureName.contains("掠夺者前哨站")) + return "掠夺者前哨站"; + if (structureName.contains("废弃矿井") || structureName.contains("恶地废弃矿井")) + return "地下"; + // 要塞可生成在任何主世界群系下,返回"主世界" + if (structureName.contains("要塞")) + return "主世界"; + // 远古城市是结构,应分类为"远古城市" + if (structureName.contains("远古城市")) + return "远古城市"; + if (structureName.contains("远古遗迹") || structureName.contains("古迹废墟")) + return "远古遗迹"; + if (structureName.contains("试炼密室")) + return "试炼密室"; + if (structureName.contains("海底神殿") || structureName.contains("海洋神殿")) + return "海洋"; + if (structureName.contains("海底废墟") || structureName.contains("冻洋废墟") || structureName.contains("暖洋废墟")) + return "海洋"; + if (structureName.contains("沉船")) + return "海洋"; + if (structureName.contains("埋藏的宝箱") || structureName.contains("埋藏的宝藏")) + return "海洋"; + if (structureName.contains("废弃传送门")) + return "平原"; + if (structureName.contains("化石")) + return "主世界"; + + // 下界结构 + if (structureName.contains("下界要塞")) + return "下界"; + if (structureName.contains("猪灵堡垒") || structureName.contains("堡垒遗迹")) + return "下界"; + if (structureName.contains("玄武岩结构") || structureName.contains("下界化石")) + return "下界"; + + // 末地结构 + if (structureName.contains("末地城")) + return "末地"; + if (structureName.contains("末地船")) + return "末地"; + + return ""; + } + + +// ========== 解析为区块 ========== + + private List
parseIntoSections( + List lines) { + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + return parseHashSections(lines); + } else if (hasDash) { + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + continue; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseHashSections(converted); + } + return new ArrayList<>(); + } + + private List
parseHashSections( + List lines) { + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add(new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } else if (t.isEmpty()) { + // skip + } else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty() + && curTitle != null) { + curLines.add(cond); + } + } + } + if (curTitle != null) { + sections.add(new Section( + curTitle, curLines)); + } + return sections; + } + + private List
buildFlatSections( + List lines, + String category) { + List conditions = + new ArrayList<>(); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + conditions.add(cond); + } + } + if (conditions.isEmpty()) + return new ArrayList<>(); + List
list = + new ArrayList<>(); + list.add(new Section( + "第一阶段", conditions)); + return list; + } + + // ========== 文件加载入口 ========== + + private void loadAllQuests() { + loadCategory("新人任务"); + loadCategory("主线任务"); + loadCategory("支线任务"); + } + + private void loadCategory(String name) { + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 扫描中..."); + File folder = new File( + plugin.getDataFolder(), + name); + String path = "plugins/Sdf1_login/" + + name + "/"; + + if (!folder.exists()) { + folder.mkdirs(); + plugin.getLogger().warning( + "[QuestTracker] 已创建目录: " + + path); + questCache.put(name, + new ArrayList<>()); + return; + } + + File[] mdFiles = folder.listFiles( + (d, n) -> n.endsWith(".md")); + + if (mdFiles == null + || mdFiles.length == 0) { + plugin.getLogger().warning( + "[QuestTracker] " + path + + " 下无 .md 文件"); + questCache.put(name, + new ArrayList<>()); + return; + } + Arrays.sort(mdFiles); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 发现 " + mdFiles.length + + " 个文件,开始扫描..."); + + // 合并所有文件的阶段 + TreeMap merged = + new TreeMap<>(); + int fileCount = 0; + + for (int fi = 0; + fi < mdFiles.length; fi++) { + File file = mdFiles[fi]; + List lines = + readFileLines(file); + + if (lines.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 空文件,跳过"); + continue; + } + + List
sections = + parseIntoSections(lines); + + if (sections.isEmpty()) { + sections = buildFlatSections( + lines, name); + } + + if (sections.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 无法识别,跳过"); + continue; + } + + // 每个文件独立处理阶段号 + int defaultNum = fi + 1; + buildSingleFileStages( + sections, name, + merged, defaultNum); + + fileCount++; + } + + if (merged.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + name + + ": 无有效任务数据"); + questCache.put(name, + new ArrayList<>()); + return; + } + + // 日志 + for (Map.Entry en + : merged.entrySet()) { + int num = en.getKey() >= 1000 + ? en.getKey() - 999 + : en.getKey(); + plugin.getLogger().info( + "[QuestTracker] " + name + + " 阶段" + num + + " \"" + + en.getValue().displayName + + "\" → 条件" + + en.getValue() + .conditions.size() + + "项, 详情" + + en.getValue() + .details.size() + + "项, 奖励" + + en.getValue() + .rewards.size() + + "项"); + } + + List list = + new ArrayList<>( + merged.values()); + questCache.put(name, list); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 解析完成 → " + + list.size() + " 个阶段" + + " (来自 " + fileCount + + " 个文件)"); + } + + private Integer extractStageNumber( + String title) { + if (title == null) return null; + String t = title.toLowerCase().trim(); + + // 含阶段/stage/关/chapter 关键词 + boolean isStage = + t.contains("阶段") + || t.contains("stage") + || t.contains("关卡") + || t.contains("章节") + || t.contains("chapter"); + + if (isStage) { + String digits = title + .replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + try { + return Integer.parseInt(digits); + } catch (NumberFormatException e) { + // fall through + } + } + return parseChineseNumber(title); + } + + // 无关键词但以数字开头(如 "1. 挖掘") + String trimmed = t + .replaceAll("^[.、\\s]+", "") + .trim(); + if (trimmed.length() > 0 + && Character.isDigit( + trimmed.charAt(0))) { + String num = trimmed + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) { + try { + return Integer.parseInt(num); + } catch (NumberFormatException e) { + // fall through + } + } + } + + return null; + } +// ========== 领取记录 ========== + + /** + * 标记已领取某阶段奖励 + */ + public void markClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return; + // 用阶段编号代替显示名,保证稳定 + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + storage.setCondition(player, key); + } + + public boolean hasClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return false; + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + return toBool( + storage.getCondition( + player, key)); + } + + private int parseChineseNumber( + String title) { + String[] cn = {"一", "二", "三", + "四", "五", "六", "七", + "八", "九", "十"}; + if (title.contains("十")) { + for (int j = 0; j < 9; j++) { + if (title.contains(cn[j])) { + return 10 + j + 1; + } + } + return 10; + } + for (int i = 0; i < 9; i++) { + if (title.contains(cn[i])) { + return i + 1; + } + } + return 0; + } + + // ========== md 解析核心 ========== + + /** + * 读取文件所有行 + */ + private List readFileLines( + File file) { + List lines = + new ArrayList<>(); + try { + BufferedReader br = + new BufferedReader( + new FileReader(file)); + String line; + while ((line = br.readLine()) + != null) { + lines.add(line); + } + br.close(); + } catch (Exception e) { + plugin.getLogger().warning( + "[QuestTracker] 读取 " + + file.getName() + + " 失败: " + + e.getMessage()); + } + return lines; + } + + /** + * 自动检测格式并解析单个文件 + */ + private List parseMdFile( + File file, String category) { + List lines = + readFileLines(file); + if (lines.isEmpty()) + return new ArrayList<>(); + + // 检测分隔符 + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) + hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + // # 分隔格式 + return parseSections( + lines, category); + } else if (hasDash) { + // ---- 分隔格式:转换为 # 后复用 + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + title = "separator"; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseSections( + converted, category); + } else { + // 无分隔符:整文件一个阶段 + return parseFlat(lines, category); + } + } + + /** + * # 分隔格式 → 区块 → 阶段 + */ + private List parseSections( + List lines, + String category) { + // 拆分为区块 + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + + // # 开头 → 新区块 + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add( + new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } + // 空行/分隔线 → 跳过 + else if (t.isEmpty() + || t.startsWith("----") + || t.startsWith("====") + || t.startsWith("***")) { + // skip + } + // 普通行 → 区块内容 + else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + curLines.add(cond); + } + } + } + // 保存最后一个区块 + if (curTitle != null) { + sections.add( + new Section( + curTitle, curLines)); + } + + // 直接转为 QuestFile 列表 + List result = + new ArrayList<>(); + int num = 1; + for (Section sec : sections) { + QuestFile qf = new QuestFile(); + qf.displayName = + extractDisplayName( + sec.title); + qf.fileName = category; + qf.conditions = + new ArrayList<>(sec.lines); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + result.add(qf); + num++; + } + return result; + } + + /** + * 无分隔符:整文件 = 一个阶段 + */ + private List parseFlat( + List lines, + String category) { + List list = + new ArrayList<>(); + QuestFile qf = makeDefaultStage( + category); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + qf.conditions.add(cond); + } + } + if (!qf.conditions.isEmpty()) { + list.add(qf); + } + return list; + } +// ========== Vault 经济奖励发放 ========== + + /** + * 发放阶段奖励 + * 含经济内容的走 Vault API或债券 + */ + public void claimRewards( + Player player, + QuestFile qf) { + if (qf == null + || qf.rewards == null + || qf.rewards.isEmpty()) { + player.sendMessage("§c无可领取的奖励"); + return; + } + + Main main = (Main) plugin; + // ★ 读取配置:economy = Vault,bonds = 债券(默认) + boolean useEconomy = "economy".equalsIgnoreCase( + main.getConfigMgr().rewardChannel); + + // Vault 经济引用(仅 useEconomy 时使用) + net.milkbowl.vault.economy.Economy econ = null; + if (useEconomy) { + econ = main.getEconomy(); + } + + int sentCount = 0; + + for (String reward : qf.rewards) { + String rt = reward.trim(); + if (rt.isEmpty()) continue; + + // 检查可选奖励标记 + if (hasOptionalMarker(rt)) { + if (!areAllConditionsMet(player.getName(), qf)) { + player.sendMessage( + "§7[可选奖励] 需完成所有可选条件: " + rt); + continue; + } + } + + // 按 + 分割子项 + String[] parts = rt.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 尝试识别经济金额 + double amt = extractEconomyAmount(part); + if (amt > 0) { + // ★ 根据配置分发 + if (useEconomy && econ != null) { + // —— Vault 经济 —— + double before = + econ.getBalance(player); + econ.depositPlayer(player, amt); + double after = + econ.getBalance(player); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(余额: §e" + + String.format("%.2f", before) + + " §7→ §a" + + String.format("%.2f", after) + + "§7)"); + if (plugin instanceof Main) { + ((Main) plugin).getCommission() + .payCommission( + player.getName(), amt); + } + } else { + // —— 债券(默认) —— + int bondAmt = + (int) Math.round(amt); + BondManager bonds = main.getBonds(); + if (bonds != null) { + int before = bonds.getBonds( + player.getName()); + bonds.addBonds( + player.getName(), + bondAmt, + "quest_reward", + qf.displayName, + "任务系统", + "任务奖励:" + qf.displayName); + int after = bonds.getBonds( + player.getName()); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(债券: §e" + + before + + " §7→ §a" + + after + "§7)"); + } else { + player.sendMessage( + "§c债券系统不可用"); + } + } + sentCount++; + continue; + } + + // 尝试发物品 + java.util.List items = + parseRewardItems(part); + if (!items.isEmpty()) { + for (ItemStack is : items) { + player.getInventory().addItem(is); + } + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + continue; + } + + // 非经济非物品 → 仅消息 + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + } + } + + plugin.getLogger().info( + "[QuestTracker] " + player.getName() + + " 预取了 \"" + qf.displayName + + "\" 共 " + sentCount + "项奖励"); + } + + /** + * 检查奖励文本是否含可选标记 + */ + private boolean hasOptionalMarker( + String text) { + return text.contains("(可选)") + || text.contains("(进阶)") + || text.contains("【可选】") + || text.contains("【进阶】"); + } + + /** + * 检查阶段所有条件(含可选)是否全部满足 + * 用于决定可选奖励是否发放 + */ + private boolean areAllConditionsMet( + String player, QuestFile qf) { + if (qf.conditions == null + || qf.conditions.isEmpty()) + return true; + for (String cond : qf.conditions) { + if (!isConditionMet(player, cond)) + return false; + } + return true; + } + + /** + * 从奖励文本中提取经济金额 + * 支持: "500游戏币" "500" "金币500" + * "$500" "1000-5000"(随机) "1000~5000" + */ + private double extractEconomyAmount( + String text) { + String t = text.replaceAll(" ", ""); + + // 必须包含经济关键词才算经济奖励 + boolean hasEcon = + t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额") + || t.contains("发钱") + || t.contains("债券"); // ★ 新增 + + + if (!hasEcon) return 0; + + // 范围: "1000-5000" 或 "1000~5000" + Matcher rm = Pattern + .compile("(\\d+)[-~](\\d+)") + .matcher(t); + if (rm.find()) { + double min = Double.parseDouble( + rm.group(1)); + double max = Double.parseDouble( + rm.group(2)); + return min + Math.random() + * (max - min); + } + + // 纯数字(含 $ 前缀) + Matcher nm = Pattern + .compile("\\$?(\\d+(\\.\\d+)?)") + .matcher(t); + if (nm.find()) { + return Double.parseDouble( + nm.group(1)); + } + + return 0; + } + /** + * 解析奖励物品文本 + * "皮革套装*1" → 4件皮革装备 + * "木剑*1" → 1把木剑 + * "钻石*64" → 64颗钻石 + */ + private List parseRewardItems( + String text) { + List result = + new ArrayList<>(); + + // 去掉条件前缀 + // 如 "完成注册登录:皮革套装*1" + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + + // 按 + 分割多个物品 + String[] parts = + text.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 跳过经济文本 + if (extractEconomyAmount(part) > 0) + continue; + + int amount = 1; + String itemName = part; + + // 解析 *N 数量 + if (part.contains("*")) { + String[] sp = + part.split("\\*"); + itemName = sp[0].trim(); + try { + amount = Integer.parseInt( + sp[1].trim()); + } catch (Exception e) { + amount = 1; + } + } + + List mats = + matchMaterials(itemName); + for (Material m : mats) { + int give = m.name() + .contains("HELMET") + || m.name() + .contains("CHESTPLATE") + || m.name() + .contains("LEGGINGS") + || m.name() + .contains("BOOTS") + ? 1 : amount; + result.add( + new ItemStack(m, give)); + } + } + return result; + } + + /** + * 中文物品名 → Material 列表 + * 套装返回多个部件 + */ + private List matchMaterials( + String name) { + List list = + new ArrayList<>(); + String n = name.toLowerCase().trim(); + + // 套装 + if (n.contains("皮革套装") + || n.contains("皮套装")) { + list.add(Material.LEATHER_HELMET); + list.add(Material.LEATHER_CHESTPLATE); + list.add(Material.LEATHER_LEGGINGS); + list.add(Material.LEATHER_BOOTS); + return list; + } + if (n.contains("铁套装") + || n.contains("铁甲")) { + list.add(Material.IRON_HELMET); + list.add(Material.IRON_CHESTPLATE); + list.add(Material.IRON_LEGGINGS); + list.add(Material.IRON_BOOTS); + return list; + } + if (n.contains("钻石套装") + || n.contains("钻甲")) { + list.add(Material.DIAMOND_HELMET); + list.add(Material.DIAMOND_CHESTPLATE); + list.add(Material.DIAMOND_LEGGINGS); + list.add(Material.DIAMOND_BOOTS); + return list; + } + // 木制工具套装 + if (n.contains("木制工具") + || n.contains("木工具")) { + list.add(Material.WOODEN_SWORD); + list.add(Material.WOODEN_PICKAXE); + list.add(Material.WOODEN_AXE); + list.add(Material.WOODEN_SHOVEL); + return list; + } + if (n.contains("石制工具") + || n.contains("石工具")) { + list.add(Material.STONE_SWORD); + list.add(Material.STONE_PICKAXE); + list.add(Material.STONE_AXE); + list.add(Material.STONE_SHOVEL); + return list; + } + if (n.contains("铁工具")) { + list.add(Material.IRON_SWORD); + list.add(Material.IRON_PICKAXE); + list.add(Material.IRON_AXE); + list.add(Material.IRON_SHOVEL); + return list; + } + + // 单个物品 + if (n.contains("木剑")) + list.add(Material.WOODEN_SWORD); + else if (n.contains("木镐")) + list.add(Material.WOODEN_PICKAXE); + else if (n.contains("铁剑")) + list.add(Material.IRON_SWORD); + else if (n.contains("铁镐")) + list.add(Material.IRON_PICKAXE); + else if (n.contains("钻石剑")) + list.add(Material.DIAMOND_SWORD); + else if (n.contains("钻石镐")) + list.add(Material.DIAMOND_PICKAXE); + else if (n.contains("钻石")) + list.add(Material.DIAMOND); + else if (n.contains("铁锭")) + list.add(Material.IRON_INGOT); + else if (n.contains("金锭") + || n.contains("金块")) + list.add(Material.GOLD_INGOT); + else if (n.contains("煤炭")) + list.add(Material.COAL); + else if (n.contains("木棍")) + list.add(Material.STICK); + else if (n.contains("弓")) + list.add(Material.BOW); + else if (n.contains("箭")) + list.add(Material.ARROW); + else if (n.contains("床")) + list.add(Material.RED_BED); + else { + // 尝试直接匹配 Material 名 + try { + list.add(Material.valueOf( + n.toUpperCase() + .replaceAll("[^A-Z_]", + ""))); + } catch (Exception e) { + // 未知物品 + } + } + return list; + } + + private QuestFile getOrCreateStage( + TreeMap map, + int key, + String category) { + if (!map.containsKey(key)) { + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + key; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + } + return map.get(key); + } + + /** + * 单个文件的区块 → 阶段 + * defaultStageNum: 文件无阶段标题时的默认编号 + */ + private void buildSingleFileStages( + List
sections, + String category, + TreeMap stageMap, + int defaultStageNum) { + Integer curKey = null; + + for (Section sec : sections) { + Integer stageNum = + extractStageNumber(sec.title); + + if (stageNum != null) { + curKey = stageNum; + } else if (curKey == null) { + curKey = defaultStageNum; + } + + QuestFile qf = getOrCreateStage( + stageMap, curKey, category); + + String displayName = + extractDisplayName(sec.title); + + // 设置显示名 + if (!displayName.isEmpty() + && qf.displayName + .startsWith("阶段 ")) { + qf.displayName = displayName; + } + + // 路由 + if (stageNum != null + && sec.lines.isEmpty()) { + if (!displayName.isEmpty()) { + qf.displayName = displayName; + } + } + else if (isRewardTitle(sec.title)) { + // 奖励 → 只进 rewards + qf.rewards.addAll(sec.lines); + } + else if (isDetailsTitle(sec.title)) { + // 详情 → 进 details + conditions + qf.details.addAll(sec.lines); + for (String line : sec.lines) { + if (!qf.conditions + .contains(line)) { + qf.conditions.add(line); + } + } + } + else if (isConditionsTitle( + sec.title)) { + // 条件 → 只进 conditions + qf.conditions.addAll(sec.lines); + } + else { + // 其他 → 条件 + qf.conditions.addAll(sec.lines); + } + } + } + + /** + * "主线任务-斗志昂扬" → "斗志昂扬" + * "支线-钓鱼" → "钓鱼" + * "阶段 1" → "阶段 1" + * "第一阶段" → "第一阶段" + */ + private String extractDisplayName( + String title) { + if (title == null) return ""; + // 按 - 或 — 或 – 分割 + String[] parts = title.split( + "\\s*[-—–]\\s*", 2); + if (parts.length >= 2) { + String right = parts[1].trim(); + if (!right.isEmpty()) { + return right; + } + } + return title; + } + private DatabaseManager getDb() { + return ((Main) plugin).getDb(); + } + + private boolean isDetailsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("任务详情") + || t.contains("详情") + || t.contains("任务说明") + || t.contains("任务目标") + || t.contains("description") + || t.contains("任务要求"); + } + + private boolean isConditionsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("解锁条件") + || t.contains("条件") + || t.contains("前置条件") + || t.contains("前置要求") + || t.contains("解锁") + || t.contains("condition"); + } + + + private void ensureStage( + TreeMap map, + String category, + QuestFile current, + int counter) { + if (current != null) return; + // 用大数字确保排在有编号阶段后面 + int key = 1000 + counter; + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + (counter + 1); + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + // 更新调用方的引用(通过返回值) + // 这里用 map 操作,current 无法直接更新 + // 所以改用返回值方式 + } + /** + * 自动将条件中的奖励内容移到 rewards + */ + private void separateRewards(QuestFile qf) { + List remaining = + new ArrayList<>(); + for (String line : qf.conditions) { + if (isRewardContent(line)) { + if (!qf.rewards.contains(line)) { + qf.rewards.add(line); + } + } else { + remaining.add(line); + } + } + qf.conditions = remaining; + } + private boolean checkStageDependency( + String player, + String condition) { + Matcher m = Pattern.compile( + "第([一二三四五六七八九十\\d]+)" + + "阶段") + .matcher(condition); + + while (m.find()) { + String numStr = m.group(1); + int stageNum = + parseChineseNumber(numStr); + if (stageNum == 0) { + try { + stageNum = + Integer.parseInt(numStr); + } catch (Exception e) { + return false; + } + } + + boolean stageDone = false; + for (List quests + : questCache.values()) { + for (QuestFile qf : quests) { + Integer qfNum = + extractStageNumber( + qf.displayName); + if (qfNum != null + && qfNum == stageNum) { + if (isStageComplete(player, + qf.conditions)) { + stageDone = true; + } + break; + } + } + if (stageDone) break; + } + + if (!stageDone) return false; + } + return true; + } + + /** + * 判断一行内容是否为奖励 + */ + private boolean isRewardContent( + String line) { + String l = line.toLowerCase(); + // 含货币关键词 + if (l.contains("游戏币") + || l.contains("金币") + || l.contains("积分") + || l.contains("钻石") + || l.contains("发钱") + || l.contains("发放") + || l.contains("经验") + || l.contains("等级") + || l.contains("经济") + || l.contains("余额") + || l.contains("块钱")) + return true; + // 纯数字+单位(如 "500游戏币") + if (l.matches( + ".*\\d+\\s*(游戏币|金币|元|积分|" + + "钻石|块钱|经验|级|经济|余额).*")) + return true; + // $前缀(如 "$500") + if (l.matches(".*\\$\\d+.*")) + return true; + // 范围格式(如 "1000-5000" 或 "1000~5000") + if (l.matches(".*\\d+[-~]\\d+.*") + && !l.contains("阶段") + && !l.contains("stage")) + return true; + return false; + } + + // ========== 标题分类 ========== + + private boolean isStageTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("阶段") + || t.contains("stage") + || t.contains("第一") + || t.contains("第二") + || t.contains("第三") + || t.contains("第四") + || t.contains("第五"); + } + + private boolean isRewardTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("奖励") + || t.contains("奖品") + || t.contains("报酬") + || t.contains("reward") + || t.contains("奖赏") + || t.contains("礼包内容") + || t.contains("礼包") + || t.contains("物品奖励"); + } + + + private QuestFile makeDefaultStage( + String category) { + QuestFile qf = new QuestFile(); + qf.displayName = "第一阶段"; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + return qf; + } + + private static class Section { + final String title; + final List lines; + Section(String title, + List lines) { + this.title = title; + this.lines = lines; + } + } + + // ========== 公开查询 ========== + + public List getQuests( + String category) { + List list = + questCache.get(category); + return list != null ? list + : new ArrayList<>(); + } + + public int getCompletedStageCount( + String player, + List quests) { + int count = 0; + if (quests == null) return 0; + for (QuestFile qf : quests) { + if (isStageComplete(player, + qf.conditions)) { + count++; + } + } + return count; + } + + public QuestFile getCurrentStage( + String player, + List quests) { + if (quests == null) return null; + for (QuestFile qf : quests) { + if (!isStageComplete( + player, qf.conditions)) { + return qf; + } + } + return null; + } + + public int getStageNumber( + List quests, + QuestFile qf) { + if (quests == null || qf == null) + return 0; + return quests.indexOf(qf) + 1; + } + + public int getStageNumber( + QuestFile qf) { + if (qf == null) return 0; + List quests = + getQuests(qf.fileName); + return getStageNumber(quests, qf); + } + + public boolean isStageCompleted( + String player, + QuestFile qf) { + if (qf == null) return false; + return isStageComplete( + player, qf.conditions); + } + + public List getUnmetConditions( + String player, + List conditions) { + List unmet = + new ArrayList<>(); + if (conditions == null) return unmet; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) { + unmet.add( + getProgress( + player, cond)); + } + } + return unmet; + } + public String getProgress( + String player, String rawCond) { + if (player == null || rawCond == null) + return "§7未知"; + String key = stripOptional(rawCond); + + if (key.contains("打卡") + && key.contains("群系")) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return "§7打卡 " + biome + + "群系: §e" + + catCount + "/1"; + } + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + + if (key.contains("完成") + && key.contains("阶段")) { + boolean done = checkStageDependency( + player, key); + return done + ? "§a✓ " + key + : "§c✗ " + key; + } + + if ("完成注册登录".equals(key)) { + boolean done = getDb() != null + && getDb().userExists( + player); + return done + ? "§a✓ 完成注册登录" + : "§c✗ 完成注册登录"; + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + boolean done = val != null + && !val.toString().isEmpty(); + return done + ? "§a✓ 绑定Email" + : "§c✗ 绑定Email"; + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + needed + "小时"; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + int cur = getCounter( + player, "CHAT"); + return "§7聊天: §e" + + cur + "/1"; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return "§7方块操作: §e" + + total + "/" + needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return "§7连续登录: §e" + + streak + "/" + needed + "天"; + } + + if (key.startsWith("MINE:")) { + String[] parts = key.split(":"); + if (parts.length >= 3) { + int needed = + parseInt(parts[2]); + int cur = getCounter( + player, + "MINE:" + parts[1]); + return "§7挖 " + parts[1] + + ": §e" + cur + + "/" + needed; + } + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "VILLAGER_TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + + } + + // ===== 击杀泛解析(支持中文列表) ===== +// 匹配 "击杀10只骷髅、僵尸、苦力怕" 等 + if (key.contains("击杀") && key.contains("只")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + + String mobPart = key; + java.util.regex.Matcher killM = + java.util.regex.Pattern + .compile("击杀\\d+只(.+)") + .matcher(key); + if (killM.find()) { + mobPart = killM.group(1).trim(); + } + + String[] mobNames = mobPart + .split("[、,,,]+"); + int totalKills = 0; + StringBuilder names = new StringBuilder(); + for (String name : mobNames) { + String trimmed = name.trim(); + if (trimmed.isEmpty()) continue; + String entityType = + chineseMobToEntity(trimmed); + if (!entityType.isEmpty()) { + totalKills += getCounter( + player, + "KILL:" + entityType); + if (names.length() > 0) + names.append("/"); + names.append(trimmed); + } + } + return "§7击杀" + names + ": §e" + + totalKills + "/" + needed; + } + +// 单一KILL:xxx:N 格式(原有逻辑保留) + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + int needed = parseInt(p[2]); + int cur = getCounter(player, + "KILL:" + p[1]); + return "§7击杀" + p[1] + ": §e" + + cur + "/" + needed; + } + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "KILL_PLAYER"); + return "§7击杀玩家: §e" + + cur + "/" + needed; + } + + if (key.startsWith("CRAFT")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "CRAFT"); + return "§7合成: §e" + + cur + "/" + needed; + } + + if (key.startsWith("FISH")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "FISH"); + return "§7钓鱼: §e" + + cur + "/" + needed; + } + + if (key.startsWith("PLAYTIME")) { + String[] parts = key.split(":"); + long needed = parts.length >= 2 + ? Long.parseLong(parts[1]) + : 0; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + formatMs(needed); + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7上报村庄: §e" + + cur + "/" + needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7探索村庄: §e" + + cur + "/1"; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return "§7近7天登录: §e" + + streak + "/" + + needed + "天"; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7新手任务: §e" + + done + "/" + + quests.size(); + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7主线任务: §e" + + done + "/" + + quests.size(); + } + // ===== 泛解析:击杀类进度 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < mobTypes.size(); i++) { + total += getCounter(player, + "KILL:" + mobTypes.get(i)); + if (i > 0) names.append("/"); + names.append(reverseMobName( + mobTypes.get(i))); + } + return "§7击杀" + names + ": §e" + + total + "/" + needed; + } + } + + // ===== 泛解析:村民交易进度 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + // ===== 泛解析:采集/挖矿进度 ===== + if (key.contains("采集") + || key.contains("挖")) { + int needed = + parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < matTypes.size(); i++) { + total += getCounter( + player, + "MINE:" + matTypes + .get(i)); + if (i > 0) + names.append("/"); + names.append( + translateMaterialName( + matTypes + .get(i))); + } + return "§7采集" + names + + ": §e" + total + + "/" + needed; + } + } + + // ===== 泛解析:合成进度 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "CRAFT"); + return "§7合成: §e" + cur + "/" + needed; + } + + // ===== 泛解析:钓鱼进度 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "FISH"); + return "§7钓鱼: §e" + cur + "/" + needed; + } + + return "§7" + key; + } + private String translateMaterialName( + String name) { + switch (name) { + case "COAL_ORE": + return "煤矿石"; + case "DEEPSLATE_COAL_ORE": + return "深层煤矿石"; + case "IRON_ORE": + return "铁矿石"; + case "DEEPSLATE_IRON_ORE": + return "深层铁矿石"; + case "COPPER_ORE": + return "铜矿石"; + case "DEEPSLATE_COPPER_ORE": + return "深层铜矿石"; + case "GOLD_ORE": + return "金矿石"; + case "DEEPSLATE_GOLD_ORE": + return "深层金矿石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "DEEPSLATE_DIAMOND_ORE": + return "深层钻石矿石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "DEEPSLATE_EMERALD_ORE": + return "深层绿宝石矿石"; + case "LAPIS_ORE": + return "青金石矿石"; + case "DEEPSLATE_LAPIS_ORE": + return "深层青金石矿石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "DEEPSLATE_REDSTONE_ORE": + return "深层红石矿石"; + case "NETHER_GOLD_ORE": + return "下界金矿石"; + case "NETHER_QUARTZ_ORE": + return "下界石英矿石"; + case "ANCIENT_DEBRIS": + return "远古残骸"; + case "COAL": + return "煤炭"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "RAW_COPPER": + return "粗铜"; + case "DIAMOND": + return "钻石"; + case "EMERALD": + return "绿宝石"; + case "LAPIS_LAZULI": + return "青金石"; + case "REDSTONE": + return "红石"; + case "QUARTZ": + return "石英"; + default: + return name; + } + } + + /** + * 中文物种名 → Bukkit EntityType 名 + * 支持击杀泛解析的模糊匹配 + */ + private String chineseMobToEntity(String cn) { + String n = cn.toLowerCase().trim(); + + // 直接别名表 + Map mobMap = new HashMap<>(); + mobMap.put("骷髅", "SKELETON"); + mobMap.put("僵尸", "ZOMBIE"); + mobMap.put("苦力怕", "CREEPER"); + mobMap.put("蜘蛛", "SPIDER"); + mobMap.put("洞穴蜘蛛", "CAVE_SPIDER"); + mobMap.put("小白", "SKELETON"); + mobMap.put("末影人", "ENDERMAN"); + mobMap.put("enderman", "ENDERMAN"); + mobMap.put("猪灵", "PIGLIN"); + mobMap.put("猪灵蛮兵", "PIGLIN_BRUTE"); + mobMap.put("恶魂", "GHAST"); + mobMap.put("凋灵骷髅", "WITHER_SKELETON"); + mobMap.put("烈焰人", "BLAZE"); + mobMap.put("史莱姆", "SLIME"); + mobMap.put("岩浆怪", "MAGMA_CUBE"); + mobMap.put("女巫", "WITCH"); + mobMap.put("蠹虫", "SILVERFISH"); + mobMap.put("潜影贝", "SHULKER"); + mobMap.put("守卫者", "GUARDIAN"); + mobMap.put("远古守卫者", "ELDER_GUARDIAN"); + mobMap.put("溺尸", "DROWNED"); + mobMap.put("僵尸村民", "ZOMBIE_VILLAGER"); + mobMap.put("尸壳", "HUSK"); + mobMap.put("流浪者", "STRAY"); + mobMap.put("幻翼", "PHANTOM"); + mobMap.put("掠夺者", "PILLAGER"); + mobMap.put("卫道士", "VINDICATOR"); + mobMap.put("唤魔者", "EVOKER"); + mobMap.put("恼鬼", "VEX"); + mobMap.put("劫掠兽", "RAVAGER"); + mobMap.put("蜘蛛骑士", "SPIDER_JOCKEY"); + mobMap.put("凋灵", "WITHER"); + mobMap.put("末影龙", "ENDER_DRAGON"); + + // 精确匹配 + if (mobMap.containsKey(n)) { + return mobMap.get(n); + } + + // 模糊匹配(包含关系) + for (Map.Entry en + : mobMap.entrySet()) { + if (n.contains(en.getKey()) + || en.getKey().contains(n)) { + return en.getValue(); + } + } + + // 尝试直接用英文名 + try { + return org.bukkit.entity.EntityType + .valueOf(n.toUpperCase() + .replace(" ", "_")) + .name(); + } catch (Exception e) { + return ""; + } + } + + public List checkInBiome( + String player, + String rawBiomeKey, + String worldName, + double x, double y, double z) { + List matched = + new ArrayList<>(); + + // 32格去重 + String posKey = + player + "_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + String lastPos = + lastCheckInPos.get(posKey); + if (curPos.equals(lastPos)) { + plugin.getLogger().info( + "[QuestTracker] 打卡忽略: " + + player + + " 32格内重复"); + return matched; + } + lastCheckInPos.put(posKey, curPos); + + String category = + mapBiomeCategory( + rawBiomeKey, worldName); + + // ===== 结构检测优先于Y轴"地下"判断 ===== + // 要塞等结构可生成在任何主世界群系下,不应固定为"地下" + // 使用 PaperMC API 检测玩家所在位置的结构 + String structureCat = null; + try { + structureCat = detectStructureAt( + worldName, x, y, z); + } catch (Exception ignored) {} + + if (structureCat != null + && !structureCat.isEmpty() + && !"地下".equals(structureCat)) { + // 结构检测成功且非"地下"类,结构分类优先 + category = structureCat; + plugin.getLogger().info( + "[QuestTracker] 结构覆盖: " + + category + + " (结构: " + + structureCat + ")"); + } else { + // 未检测到有效结构,使用Y轴检测判定地下 + // 地下检测:Y低于表面则归类为地下 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + if (structureCat != null + && !structureCat.isEmpty()) { + plugin.getLogger().info( + "[QuestTracker] 结构" + + structureCat + + " 使用Y轴判定"); + } else { + plugin.getLogger().info( + "[QuestTracker] 未检测到结构: world=" + + worldName + ", pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + } + } + + + // 记录总打卡 + int allCur = getCounter( + player, "biome_ALL"); + setCounter(player, "biome_ALL", + allCur + 1); + + // 记录分类打卡 + if (!category.isEmpty() + && !"主世界" + .equals(category)) { + String catKey = + "biomecat_" + category; + int catCur = getCounter( + player, catKey); + setCounter(player, catKey, + catCur + 1); + } + + // 记录具体群系 + String bioKey = + "biome_" + rawBiomeKey; + int bioCur = getCounter( + player, bioKey); + setCounter(player, bioKey, + bioCur + 1); + + // 检查所有任务条件是否匹配 + List categories = + Arrays.asList( + "新人任务", + "主线任务", + "支线任务"); + + for (String cat : categories) { + List quests = + getQuests(cat); + for (QuestFile qf : quests) { + if (isStageCompleted( + player, qf)) + continue; + for (String cond + : qf.conditions) { + if (isOptional(cond)) + continue; + String condKey = + stripOptional(cond); + if (!condKey + .contains("打卡")) + continue; + // 支持群系和结构两种打卡条件 + boolean isBiomeCond = + condKey.contains("群系") + || condKey + .contains("风景点"); + boolean isStructureCond = + condKey.contains("结构") + || condKey + .contains("村庄") + || condKey + .contains("神殿") + || condKey + .contains("神庙") + || condKey + .contains("小屋") + || condKey + .contains("雪屋") + || condKey + .contains("府邸") + || condKey + .contains("前哨站") + || condKey + .contains("矿井") + || condKey + .contains("要塞") + || condKey + .contains("城市") + || condKey + .contains("废墟") + || condKey + .contains("密室") + || condKey + .contains("沉船") + || condKey + .contains("宝箱") + || condKey + .contains("传送门") + || condKey + .contains("化石") + || condKey + .contains("堡垒"); + if (!isBiomeCond + && !isStructureCond) + continue; + String target = + extractBiome(condKey); + plugin.getLogger().info( + "[QuestTracker] 条件匹配: condKey=\"" + + condKey + "\", target=\"" + + target + "\", category=\"" + + category + "\", isBiomeCond=" + + isBiomeCond + ", isStructureCond=" + + isStructureCond); + boolean hit = false; + if ("ALL".equals(target) + || "风景点" + .equals(target)) { + hit = true; + } else if (!target.isEmpty() + && target.equals( + category)) { + hit = true; + } + if (hit && !matched + .contains(cond)) { + matched.add(cond); + } + } + } + } + + return matched; + } + + /* + * 打卡入口:报告坐标 → 32格去重 → 匹配条件 → 记录 + */ + public String checkInAtPosition( + String player, String worldName, + double x, double y, double z) { + + List history = + loadCheckInPositions(player); + for (double[] pos : history) { + double dist = Math.sqrt( + Math.pow(x - pos[0], 2) + + Math.pow(y - pos[1], 2) + + Math.pow(z - pos[2], 2)); + if (dist <= 32) { + return "§c距离上次打卡点太近(" + + String.format( + "%.0f", dist) + + "格)"; + } + } + + // ===== 获取群系和分类 ===== + String biome = "minecraft:plains"; + String category = "平原"; + String structureCat = null; + try { + org.bukkit.World w = + Bukkit.getWorld(worldName); + if (w != null) { + org.bukkit.Location loc = + new org.bukkit.Location( + w, + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + biome = loc.getBlock() + .getBiome().getKey() + .toString(); + category = mapBiomeCategory( + biome, worldName); + + // ===== 结构检测优先于Y轴"地下"判断 ===== + structureCat = + detectStructureAt( + worldName, x, y, z); + plugin.getLogger().info( + "[QuestTracker] checkInAtPosition 结构检测: " + + structureCat + " at " + + String.format("%.0f,%.0f,%.0f", x, y, z)); + + if (structureCat != null + && !structureCat.isEmpty() + && !"地下".equals(structureCat)) { + // 结构检测成功且非"地下"类,结构分类优先 + category = structureCat; + } else { + // 未检测到有效结构,使用Y轴检测判定地下 + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + + if ("下界".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.NETHER) { + category = "下界"; + } else if ("末地".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.THE_END) { + category = "末地"; + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 群系读取失败: " + + ex.getMessage()); + } + + plugin.getLogger().info( + "[QuestTracker] 打卡: " + + player + + " world=" + worldName + + " biome=" + biome + + " cat=" + category + + " pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + + saveCheckInPosition( + player, x, y, z); + recordCheckIn( + player, biome, category, + worldName, x, y, z); + List matched = + checkInBiome( + player, biome, + worldName, x, y, z); + + StringBuilder sb = new StringBuilder(); + sb.append("§a打卡成功!群系: §e") + .append(category); + if (structureCat != null && !structureCat.isEmpty()) { + sb.append("\n§a结构: §e").append(structureCat); + } + if (!matched.isEmpty()) { + sb.append("\n§a完成任务条件:"); + for (String m : matched) { + String qn = + findQuestName(m); + sb.append("\n§7• "); + if (qn != null) { + sb.append("§e[") + .append(qn) + .append("§7] §f"); + } + sb.append(m); + } + } + return sb.toString(); + } + + + /** + * 根据条件文本查找所属任务名 + */ + private String findQuestName( + String condition) { + for (String cat : Arrays.asList( + "新人任务", + "主线任务", + "支线任务")) { + for (QuestFile qf : + getQuests(cat)) { + if (qf.conditions.contains( + condition)) { + return qf.displayName; + } + } + } + return null; + } + + + /** + * 加载玩家所有历史打卡坐标 + * 格式: x1,y1,z1;x2,y2,z2;... + */ + private List loadCheckInPositions( + String player) { + List list = new ArrayList<>(); + Object val = storage.getCondition( + player, "checkin_all_pos"); + if (val == null) return list; + String data = val.toString(); + if (data.isEmpty()) return list; + String[] records = data.split(";"); + for (String r : records) { + String[] parts = r.split(","); + if (parts.length >= 3) { + try { + list.add(new double[]{ + Double.parseDouble(parts[0]), + Double.parseDouble(parts[1]), + Double.parseDouble(parts[2])}); + } catch (NumberFormatException e) { + // 跳过损坏数据 + } + } + } + return list; + } + + /** + * 保存打卡坐标(追加到已有记录) + */ + private void saveCheckInPosition( + String player, double x, + double y, double z) { + List list = + loadCheckInPositions(player); + list.add(new double[]{x, y, z}); + StringBuilder sb = new StringBuilder(); + for (double[] pos : list) { + if (sb.length() > 0) sb.append(";"); + sb.append(pos[0]).append(",") + .append(pos[1]).append(",") + .append(pos[2]); + } + storage.setConditionRaw( + player, + "checkin_all_pos", + sb.toString()); + } + // ========== 中文生物名 → EntityType 映射 ========== + private static final Map + MOB_NAME_MAP = new HashMap<>(); + static { + MOB_NAME_MAP.put("洞穴蜘蛛", "CAVE_SPIDER"); + MOB_NAME_MAP.put("骷髅", "SKELETON"); + MOB_NAME_MAP.put("僵尸", "ZOMBIE"); + MOB_NAME_MAP.put("苦力怕", "CREEPER"); + MOB_NAME_MAP.put("蜘蛛", "SPIDER"); + MOB_NAME_MAP.put("末影人", "ENDERMAN"); + MOB_NAME_MAP.put("女巫", "WITCH"); + MOB_NAME_MAP.put("烈焰人", "BLAZE"); + MOB_NAME_MAP.put("僵尸猪灵", + "ZOMBIFIED_PIGLIN"); + MOB_NAME_MAP.put("猪灵", "PIGLIN"); + MOB_NAME_MAP.put("恶魂", "GHAST"); + MOB_NAME_MAP.put("凋灵骷髅", + "WITHER_SKELETON"); + MOB_NAME_MAP.put("史莱姆", "SLIME"); + MOB_NAME_MAP.put("岩浆怪", "MAGMA_CUBE"); + MOB_NAME_MAP.put("溺尸", "DROWNED"); + MOB_NAME_MAP.put("尸壳", "HUSK"); + MOB_NAME_MAP.put("流髑", "STRAY"); + MOB_NAME_MAP.put("幻翼", "PHANTOM"); + MOB_NAME_MAP.put("守卫者", "GUARDIAN"); + MOB_NAME_MAP.put("远古守卫者", + "ELDER_GUARDIAN"); + MOB_NAME_MAP.put("掠夺者", "PILLAGER"); + MOB_NAME_MAP.put("卫道士", "VINDICATOR"); + MOB_NAME_MAP.put("唤魔者", "EVOKER"); + MOB_NAME_MAP.put("恼鬼", "VEX"); + MOB_NAME_MAP.put("劫掠兽", "RAVAGER"); + MOB_NAME_MAP.put("潜影贝", "SHULKER"); + MOB_NAME_MAP.put("末影螨", "ENDERMITE"); + MOB_NAME_MAP.put("凋灵", "WITHER"); + MOB_NAME_MAP.put("末影龙", "ENDER_DRAGON"); + MOB_NAME_MAP.put("监守者", "WARDEN"); + MOB_NAME_MAP.put("嗅探兽", "SNIFFER"); + MOB_NAME_MAP.put("僵尸村民", + "ZOMBIE_VILLAGER"); + } + + /** + * 从条件文本中提取所有匹配的生物类型 + * 按名称长度降序匹配,避免短名误匹配 + */ + private List extractMobTypes( + String text) { + List types = new ArrayList<>(); + List> sorted = + new ArrayList<>( + MOB_NAME_MAP.entrySet()); + sorted.sort((a, b) -> + b.getKey().length() + - a.getKey().length()); + String remaining = text; + for (Map.Entry entry + : sorted) { + if (remaining.contains( + entry.getKey())) { + if (!types.contains( + entry.getValue())) { + types.add(entry.getValue()); + } + remaining = remaining.replace( + entry.getKey(), ""); + } + } + return types; + } + + /** + * EntityType → 中文名 + */ + private String reverseMobName( + String entityType) { + for (Map.Entry entry + : MOB_NAME_MAP.entrySet()) { + if (entry.getValue() + .equals(entityType)) { + return entry.getKey(); + } + } + return entityType; + } + + /** + * 从条件文本中提取矿物名 + * "采集任意矿物(煤炭除外)1组" + * → [COAL_ORE, IRON_ORE, GOLD_ORE, ...] + */ + private List extractMaterialTypes( + String text) { + List types = new ArrayList<>(); + // 排除列表 + List excludes = + new ArrayList<>(); + if (text.contains("煤炭除外")) { + excludes.add("COAL"); + excludes.add("COAL_ORE"); + excludes.add("DEEPSLATE_COAL_ORE"); + } + // 通配符:"矿物" "矿石" + if (text.contains("矿物") + || text.contains("矿石")) { + String[] ores = { + "COAL_ORE", + "DEEPSLATE_COAL_ORE", + "IRON_ORE", + "DEEPSLATE_IRON_ORE", + "COPPER_ORE", + "DEEPSLATE_COPPER_ORE", + "GOLD_ORE", + "DEEPSLATE_GOLD_ORE", + "DIAMOND_ORE", + "DEEPSLATE_DIAMOND_ORE", + "EMERALD_ORE", + "DEEPSLATE_EMERALD_ORE", + "LAPIS_ORE", + "DEEPSLATE_LAPIS_ORE", + "REDSTONE_ORE", + "DEEPSLATE_REDSTONE_ORE", + "NETHER_GOLD_ORE", + "NETHER_QUARTZ_ORE", + "ANCIENT_DEBRIS" + }; + for (String ore : ores) { + if (!excludes.contains(ore)) { + types.add(ore); + } + } + return types; + } + // 单个矿物名 + if (text.contains("煤炭")) + types.add("COAL"); + if (text.contains("铁矿") + || text.contains("铁锭")) + types.add("RAW_IRON"); + if (text.contains("金矿") + || text.contains("金锭")) + types.add("RAW_GOLD"); + if (text.contains("钻石")) + types.add("DIAMOND"); + if (text.contains("绿宝石")) + types.add("EMERALD"); + if (text.contains("青金石")) + types.add("LAPIS_LAZULI"); + if (text.contains("红石")) + types.add("REDSTONE"); + if (text.contains("铜")) + types.add("RAW_COPPER"); + if (text.contains("下界石英") + || text.contains("石英")) + types.add("QUARTZ"); + return types; + } + + + /** + * 从条件文本中提取目标群系分类 + * "打卡上报地狱任意群系" → "下界" + * "打卡任意海洋群系" → "海洋" + * "打卡任意群系" → "ALL" + * "打卡任意风景点" → "" + * "打卡村庄" → "村庄" + * "打卡沙漠神殿" → "沙漠" + */ + private String extractBiome(String text) { + if (text == null) return ""; + + // 优先匹配结构名称(更具体) + String structureResult = + mapStructureCategory(text); + if (!structureResult.isEmpty()) { + return structureResult; + } + + // 逐个匹配分类关键词 + for (Map.Entry en + : CATEGORY_KEYWORDS.entrySet()) { + if (text.contains(en.getKey())) { + return en.getValue(); + } + } + if (text.contains("风景点") + || text.contains("风景")) { + return "ALL"; + } + + // 有"群系"但无具体分类 → ALL + if (text.contains("群系")) { + return "ALL"; + } + return ""; + } + + + // ========== 标记条件 ========== + + public void markConditionComplete( + String player, + String conditionName) { + if (player == null + || conditionName == null) + return; + storage.setCondition( + player, conditionName); + } + + // ========== 事件回调 ========== + + public void onBlockMined( + String player, String material) { + String key = "MINE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + // 总计数 + setCounter(player, "MINE:ALL", + getCounter(player, "MINE:ALL") + + 1); + } + public boolean onReportVillage( + String player, + String worldName, + double x, double y, double z) { + String dbKey = "village_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + Object lastObj = + storage.getCondition( + player, dbKey); + String lastPos = lastObj != null + ? lastObj.toString() : ""; + if (curPos.equals(lastPos)) { + return false; + } + storage.setConditionRaw( + player, dbKey, curPos); + + String key = "REPORT_VILLAGE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + return true; + } + + public void onMobKill( + String player, String mobType) { + String key = "KILL:" + mobType; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayerKill( + String player) { + String key = "KILL_PLAYER"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onItemCrafted( + String player) { + String key = "CRAFT"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onFishCaught( + String player) { + String key = "FISH"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayTime( + String player, long ms) { + String key = "PLAYTIME"; + long cur = getCounterLong( + player, key); + setCounter(player, key, cur + ms); + } + + // ========== 内部:条件判定 ========== + + private boolean isStageComplete( + String player, + List conditions) { + if (conditions == null + || conditions.isEmpty()) + return true; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) + return false; + } + return true; + } + + public boolean isConditionMet( + String player, + String rawCond) { + if (player == null + || rawCond == null) + return false; + + String key = stripOptional(rawCond); + + // 无条件类文本直接通过 + if (key.equals("无前级条件") + || key.equals("无条件") + || key.equals("无") + || key.equals("none") + || key.equals("无需条件") + || key.equals("无解锁条件")) { + return true; + } + + if (key.contains("打卡") + && (key.contains("群系") + || key.contains("风景点") + || key.contains("结构") + || key.contains("村庄") + || key.contains("神殿") + || key.contains("神庙") + || key.contains("小屋") + || key.contains("雪屋") + || key.contains("府邸") + || key.contains("前哨站") + || key.contains("矿井") + || key.contains("要塞") + || key.contains("城市") + || key.contains("废墟") + || key.contains("密室") + || key.contains("废墟") + || key.contains("沉船") + || key.contains("宝箱") + || key.contains("传送门") + || key.contains("化石") + || key.contains("堡垒"))) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return total >= 1; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return catCount >= 1; + } + return getCounter( + player, "biome_ALL") >= 1; + } + + if (key.contains("完成") + && key.contains("阶段")) { + return checkStageDependency( + player, key); + } + + if ("完成注册登录".equals(key)) { + return getDb() != null + && getDb().userExists( + player); + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + return val != null + && !val.toString().isEmpty(); + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return cur >= needed * 3600000L; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + return getCounter( + player, "CHAT") >= 1; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return total >= needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return streak >= needed; + } + + if (key.startsWith("MINE:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "MINE:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "KILL:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "VILLAGER_TRADE") + >= needed; + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter(player, + "KILL_PLAYER") >= n; + } + + if (key.startsWith("CRAFT")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "CRAFT") >= n; + } + + if (key.startsWith("FISH")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "FISH") >= n; + } + + if (key.startsWith("PLAYTIME")) { + String[] p = key.split(":"); + long n = p.length >= 2 + ? Long.parseLong(p[1]) : 0; + return getCounterLong(player, + "PLAYTIME") >= n; + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "REPORT_VILLAGE") + >= needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + return getCounter( + player, "REPORT_VILLAGE") + >= 1; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return streak >= needed; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + // ===== 泛解析:击杀类中文条件 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + for (String mobType : mobTypes) { + total += getCounter(player, + "KILL:" + mobType); + } + return total >= needed; + } + } + + // ===== 泛解析:村民交易条件 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "TRADE") + >= needed; + } + + // ===== 泛解析:采集/挖矿类条件 ===== + if (key.contains("采集") || key.contains("挖")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + for (String mat : matTypes) { + total += getCounter(player, + "MINE:" + mat); + } + return total >= needed; + } + } + + // ===== 泛解析:合成类条件 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "CRAFT") + >= needed; + } + + // ===== 泛解析:钓鱼类条件 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "FISH") + >= needed; + } + + return false; + } + + public void debugLogin(String player) { + try { + var user = getDb().getUser(player); + if (user != null) { + plugin.getLogger().info( + "[QuestTracker] " + + "=== " + player + + " 数据库全部字段 ==="); + for (var entry + : user.entrySet()) { + plugin.getLogger().info( + "[QuestTracker] " + + entry.getKey() + + " = " + + entry.getValue() + + " (" + + (entry.getValue() + != null + ? entry.getValue() + .getClass() + .getSimpleName() + : "null") + + ")"); + } + } else { + plugin.getLogger().info( + "[QuestTracker] " + + "getUser返回null: " + + player); + } + } catch (Exception e) { + plugin.getLogger().info( + "[QuestTracker] " + + "debug异常: " + + e.getMessage()); + } + } + + // ========== 内部:计数器 ========== + + private int getCounter( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + return 0; + } + private int getIntField( + String player, String field) { + try { + Object val = getDb().getField( + player, field); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val != null) { + return Integer.parseInt( + val.toString().trim()); + } + } catch (Exception e) { + // 忽略 + } + return 0; + } + + public void onVillagerTrade(String player) { + String key = "VILLAGER_TRADE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + private long getCounterLong( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).longValue(); + } + return 0; + } + + private void setCounter( + String player, String key, + int value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + private void setCounter( + String player, String key, + long value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + // ========== 工具 ========== + + private boolean toBool(Object val) { + if (val == null) return false; + if (val instanceof Boolean) + return (Boolean) val; + if (val instanceof Number) + return ((Number) val) + .intValue() != 0; + String s = String.valueOf(val); + return "1".equals(s) + || "true" + .equalsIgnoreCase(s); + } + + public boolean isOptional(String cond) { + if (cond == null) return false; + String c = cond; + return c.contains("(进阶)") + || c.contains("(可选)") + || c.contains(",可选)") + || c.contains(",可选)") + || c.contains("【可选】") + || c.contains("【进阶】"); + } + + + private String stripOptional(String cond) { + if (cond == null) return ""; + return cond.replace("(进阶)", "") + .replace("(可选)", "") + .replace(",可选)", "") + .replace(",可选)", "") + .replace("【可选】", "") + .replace("【进阶】", "") + .trim(); + } + + private int parseInt(String s) { + + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return 0; + } + } + private Object getDbField( + String player, String field) { + Main main = (Main) plugin; + return main.getDb().getField( + player, field); + } + + /** + * 从文本中提取第一个数字 + * "在线时间≥1小时" → 1 + * "破坏/放置方块100个" → 100 + */ + private int parseNumberFromText( + String text) { + java.util.regex.Matcher m = + java.util.regex.Pattern + .compile("(\\d+)") + .matcher(text); + if (m.find()) { + try { + return Integer.parseInt( + m.group(1)); + } catch (Exception e) { + return 0; + } + } + return 0; + } + public void onBlockPlaced( + String player, String material) { + String key = "PLACE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + setCounter(player, "PLACE:ALL", + getCounter(player, "PLACE:ALL") + + 1); + } + + public void onPlayerChat( + String player) { + String key = "CHAT"; + setCounter(player, key, + getCounter(player, key) + 1); + } + + private String formatMs(long ms) { + long sec = ms / 1000; + long min = sec / 60; + long hr = min / 60; + if (hr > 0) + return hr + "小时" + + (min % 60) + "分"; + if (min > 0) + return min + "分" + + (sec % 60) + "秒"; + return sec + "秒"; + } + + // ========== 数据结构 ========== + + public static class QuestFile { + public String displayName = ""; + public String fileName = ""; + public List details = + new ArrayList<>(); + public List rewards = + new ArrayList<>(); + public List conditions = + new ArrayList<>(); + + public boolean hasConditions() { + return conditions != null + && !conditions.isEmpty(); + } + } +} diff --git a/backup/Main_backup_20260606_235342.java b/backup/Main_backup_20260606_235342.java new file mode 100644 index 0000000..95903d1 --- /dev/null +++ b/backup/Main_backup_20260606_235342.java @@ -0,0 +1,5734 @@ +package Sdf1_login; + + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.io.File; + +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerInteractEvent; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.UUID; +import java.util.LinkedHashMap; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataType; +import java.util.Date; +import org.bukkit.command.CommandSender; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.scheduler.BukkitTask; + + +import net.milkbowl.vault.economy.Economy; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.scheduler.BukkitTask; + +import java.util.HashMap; + + + +public class Main extends JavaPlugin + implements CommandExecutor, Listener, + TabCompleter { + + private DatabaseManager db; + private MenuIconManager menuIconMgr; + private ConfigManager config; + private EmailManager email; + private LoginManager loginMgr; + private AFKManager afk; + private GiftManager gift; + private CheckInManager checkIn; + private InviteManager invite; + private PointsManager points; + public GUIManager gui; + private ChatInputManager chatInput; + private Economy economy; + private VerificationManager verification; + private IPGroupManager ipGroup; + private AccountRequestManager accountRequest; + private TicketManager ticket; + private ChatFilterManager chatFilter; + private GarbageManager garbage; + public RadioManager radio; + // 在 lastActivity 字段后面添加: + // [ADDED] 任务追踪系统 + private QuestTracker questTracker; + // [FIX] 垃圾箱防抖锁(记录上次操作时间戳) + private final Map garbageBusy = + new ConcurrentHashMap<>(); + // [ADDED] 群系检测节流 + private final Map lastBiomeCheck = + new ConcurrentHashMap<>(); + // [ADDED] 垃圾站宝箱物品投入次数计数器(事不过三) + private final Map treasureDiscardCount = + new ConcurrentHashMap<>(); + // 在 private WelcomeManager welcome; 字段(若不存在)添加: + private WelcomeManager welcome; + private final Set pendingAdminAuth = + new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + /* + 资源包下载部分控制,不经过RadioDownloadListener的熔断控制 + */ + private RadioDownloadListener radioDL; + private final Set chatInputActive = + ConcurrentHashMap.newKeySet(); + // 补签确认:玩家名 → "日期:总积分" + public final Map + pendingBackCheck = + new HashMap<>(); + public MenuManager menu; + + private final Set loggedIn = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Set needsPasswordChange = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Map joinTime = + new ConcurrentHashMap<>(); + private final Map joinLoc = + new HashMap<>(); + private final Map ipAutoLogin = + new HashMap<>(); + private static final long IP_SKIP_MS = 300000L; + private SalesStatsManager salesStats; + + private final Map lastActivity = + new ConcurrentHashMap<>(); + private CommissionManager commission; + // 余额操作待处理 + private String balanceTarget = null; + private boolean balanceGiveMode = false; + private BondManager bondManager; + private CDKManager cdkManager; + private UserGroupManager userGroupManager; + private CypayCommand cypayCommand; + + public BondManager getBonds() { + return bondManager; + } + + public CDKManager getCDK() { + return cdkManager; + } + + public UserGroupManager getUserGroup() { + return userGroupManager; + } + + public CypayCommand getCypay() { + return cypayCommand; + } + + // 在 Main.java 字段声明区域(bondManager 附近)添加: +// 在 bondManager 字段附近添加: + private BondPrinter bondPrinter; + private OrderManager orderManager; + + public OrderManager getOrderManager() { + return orderManager; + } + + public AreaProtection areaProtection; + + + // 钱包流水查看目标(玩家名 → 查看目标) + private final Map walletViewTarget = + new ConcurrentHashMap<>(); + + // 流水格式化 + private static final java.text.SimpleDateFormat + TX_SDF = new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + private ShopManager shopManager; //商店 + + + public CommissionManager getCommission() { + return commission; + } + + //pvp + private PVPManager pvpManager; + + private static class PwdRollback { + final String hash; + final String salt; + final long time; + + PwdRollback(String h, String s, long t) { + hash = h; + salt = s; + time = t; + } + } + + // 待删除的玩家名(仅保留最后一个) + private String pendingDeleteName = null; + private BukkitRunnable pendingDeleteTask = null; + + private final Map + pwdRollback = + new ConcurrentHashMap<>(); + private final Map menuChatField = + new java.util.concurrent.ConcurrentHashMap<>(); + private final Map menuChatIdx = + new java.util.concurrent.ConcurrentHashMap<>(); + + + public void recordPasswordChange(String name, + String oldHash, String oldSalt) { + pwdRollback.put(name, + new PwdRollback(oldHash, oldSalt, + System.currentTimeMillis())); + } + + // ===== Getters ===== + public DatabaseManager getDb() { + return db; + } + + + public ConfigManager getConfig2() { + return config; + } + + public EmailManager getEmail() { + return email; + } + + public Economy getEconomy() { + return economy; + } + + public LoginManager getLoginMgr() { + return loginMgr; + } + + public AFKManager getAfk() { + return afk; + } + + public GiftManager getGift() { + return gift; + } + + public CheckInManager getCheckIn() { + return checkIn; + } + + public InviteManager getInvite() { + return invite; + } + + public PointsManager getPoints() { + return points; + } + + public GUIManager getGui() { + return gui; + } + + public ChatInputManager getChatInput() { + return chatInput; + } + + public VerificationManager getVerification() { + return verification; + } + + public IPGroupManager getIPGroup() { + return ipGroup; + } + + public AccountRequestManager getAccountRequest() { + return accountRequest; + } + + public TicketManager getTicket() { + return ticket; + } + + public QuestTracker getQuestTracker() { + return questTracker; + } + + public ChatFilterManager getChatFilter() { + return chatFilter; + } + + public GarbageManager getGarbage() { + return garbage; + } + + public Set getLoggedIn() { + return loggedIn; + } + + public Set getNeedsPasswordChange() { + return needsPasswordChange; + } + + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + if (areaProtection != null) { + Player p = e.getPlayer(); + areaProtection.onPlayerOffline( + p.getUniqueId(), p.getName(), ""); + if (areaProtection != null) { + areaProtection.onPlayerQuit(p.getName()); + } + + } + } + + + // public RadioManager radio; + @Override + public void onEnable() { + getDataFolder().mkdirs(); + // 启动时清理.sdf1临时文件 + cleanupSdf1Files(); + salesStats = new SalesStatsManager(this); + + // ===== 1. 基础配置 ===== + config = new ConfigManager(getDataFolder()); + config.loadMessages(); + config.loadSmtp(); + config.loadSettings(); + configMgr = new ConfigManager(getDataFolder()); + configMgr.loadSettings(); + configMgr.loadMessages(); + orderManager = new OrderManager(this); + // ===== 2. 数据库 ===== + db = new DatabaseManager(getDataFolder()); + db.init(); + menuIconMgr = new MenuIconManager(this); + + + // ===== 3. 菜单 ===== + menu = new MenuManager(this); + menu.loadMenu(); + + // ===== 4. GUI(依赖 menu) ===== + gui = new GUIManager(this); + shopManager = new ShopManager(this); + Bukkit.getPluginManager().registerEvents(shopManager, this); + // shopManager.loadCategories(); // 构造器内已调用,此处重复 + + getServer().getPluginManager() + .registerEvents(gui, this); + + // ===== 5. 其他管理器 ===== + email = new EmailManager(config); + loginMgr = new LoginManager(this); + afk = new AFKManager(config, this); + gift = new GiftManager(this); + gift.loadStages(); + checkIn = new CheckInManager(this); + invite = new InviteManager(this); + points = new PointsManager(this); + chatInput = new ChatInputManager(); + verification = new VerificationManager(this); + ipGroup = new IPGroupManager(this, + config.maxAccountsPerIP); + accountRequest = + new AccountRequestManager(this); + accountRequest.setMode(config.approvalMode); + accountRequest.setAutoDelayMinutes( + config.autoApproveDelayMinutes); + ticket = new TicketManager(this); + commission = new CommissionManager(this); + questTracker = new QuestTracker(this); + chatFilter = new ChatFilterManager(this); + chatFilter.loadConfig(); + welcome = new WelcomeManager(this); + + // ===== 6. 垃圾箱 ===== + garbage = new GarbageManager(this); + garbage.init(); + garbage.loadConfig(); + garbage.startAutoCleanup(); + + // ===== 7. Radio ===== + radio = new RadioManager(this); + radio.init(); + radioDL = new RadioDownloadListener(this); + getServer().getPluginManager() + .registerEvents(radioDL, this); + + // ===== 铁块电梯 ===== + getServer().getPluginManager() + .registerEvents(new IronBlockElevator(), this); + + // ===== 8. 经济 ===== + setupEconomy(); + + // ===== 9. 注册命令 ===== + if (getCommand("sdf1_login") != null) { + getCommand("sdf1_login") + .setExecutor(this); + getCommand("sdf1_login") + .setTabCompleter(this); + } + if (getCommand("reg") != null) + getCommand("reg").setExecutor(this); + if (getCommand("login") != null) + getCommand("login").setExecutor(this); + if (getCommand("l") != null) + getCommand("l").setExecutor(this); + if (getCommand("签到") != null) + getCommand("签到") + .setExecutor(this); + if (getCommand("recycle") != null) + getCommand("recycle") + .setExecutor(this); + if (getCommand("垃圾清理") != null) + getCommand("垃圾清理") + .setExecutor(this); + /* if (getCommand("oa") != null) + getCommand("oa").setExecutor(this);*/ + if (getCommand("menu") != null) + getCommand("menu").setExecutor(this); + if (getCommand("printer") != null) { + getCommand("printer").setExecutor(this); + getCommand("shop").setExecutor(this); + getCommand("商店").setExecutor(this); + // getCommand("testorder").setExecutor(this); + if (getCommand("printer") != null) { + getCommand("printer").setExecutor(this); + getCommand("shop").setExecutor(this); + getCommand("商店").setExecutor(this); + } +// protect 命令独立注册 + if (getCommand("protect") != null) { + getCommand("protect").setExecutor(this); + getCommand("protect").setTabCompleter(this); + } + } + + // ===== 10. 注册事件 ===== + getServer().getPluginManager() + .registerEvents(this, this); + + // ===== 11. 定时任务 ===== + afk.startCheck(); + startLoginReminder(); + startTimeoutCheck(); + startPasswordReminder(); + startTicketAutoProcess(); + radio.startMainRadio(); + new BukkitRunnable() { + public void run() { + if (questTracker == null) return; + for (Player p : + Bukkit.getOnlinePlayers()) { + if (!isFrozen(p)) + questTracker.onPlayTime( + p.getName(), 30000L); + } + } + }.runTaskTimer(this, 600L, 600L); +// [新增] Printer 文件清理(每小时检测一次) + new BukkitRunnable() { + public void run() { + if (bondPrinter != null) + bondPrinter.cleanupOldFiles(); + } + }.runTaskTimer(this, 1200L, 72000L); + + // 12 ===PVP=== + pvpManager = new PVPManager(this); + getServer().getPluginManager() + .registerEvents(pvpManager, this); +// PVP区域检测定时器 + getServer().getScheduler() + .runTaskTimer(this, + () -> pvpManager.tickRegions(), + 20L, 20L); + setupPVPTab(); + + // 13 ====cypay债券==== + // 债券系统(独立DB) + bondManager = new BondManager(this); + cdkManager = new CDKManager(this); + userGroupManager = new UserGroupManager(this); + cypayCommand = new CypayCommand(this); +/*加载CDK文件 + cdkManager.loadCDKsFromDir();*/ + // cypay 命令 + if (getCommand("cypay") != null) { + getCommand("cypay").setExecutor(cypayCommand); + getCommand("cypay").setTabCompleter(cypayCommand); + } + bondPrinter = new BondPrinter(this); + // cdkManager.loadCDKsFromDir(); +//14 ====商店==== + // ★ 构造器内已加载完毕,此处重复 + // if (shopManager != null) { + // shopManager.loadCategories(); + // } + + configMgr = new ConfigManager(getDataFolder()); + configMgr.loadSettings(); + orderManager = new OrderManager(this); + +// 15 ======区域保护========= + areaProtection = new AreaProtection(this); + getServer().getPluginManager() + .registerEvents(areaProtection, this); + +// 效果清除定时器(只留一个) + getServer().getScheduler().runTaskTimer(this, () -> { + if (areaProtection != null) { + } + }, 20L, 20L); + +// 和平模式独立检测 + getServer().getScheduler().runTaskTimer(this, () -> { + if (areaProtection == null) return; + for (Player p : Bukkit.getOnlinePlayers()) { + AreaProtection.AreaConfig ac = areaProtection.getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null) { + // 无条件打印,不管 peaceMode 是啥 + /* getLogger().info("[和平定时器] " + p.getName() + + " 在[" + ac.name + + "] peaceMode=" + ac.peaceMode + + " 白名单=" + ac.peaceWhitelist);*/ + if (ac.peaceMode) { + areaProtection.banHostilesWithWhitelist(p, ac); + } + } + } + }, 40L, 40L); + areaProtection.startEnforceTask(); + if (areaProtection != null) { + areaProtection.reload(); + areaProtection.startEnforceTask(); + } + + + getLogger().info("§b[Sdf1_login]启动完毕\n§lS欢迎使用sdf1系列插件,如有问题,您可在\nGitHub和Gitee提交反馈"); + getLogger().info("sdf1系列插件包含:\nsdf1自助兑奖插件\nCY_beibao云背包\nsdf1_login登陆插件\nSdf1_game娱乐游戏插件"); + // 大字画 + getLogger().info("\n" + + " ____ _ __ _ _ _ \n" + + " / ___| __| |/ _/ | | | ___ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | | |/ _ \\ / _` | | '_ \\ \n" + + " ___) | (_| | _| | | | (_) | (_| | | | | | \n" + + " |____/ \\__,_|_| |_|___|_|\\___/ \\__, |_|_| |_| \n" + + " ____ _ __ |_____| _ |___/ _ \n" + + " / ___| __| |/ _/ |_ __ | |_ _ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | '_ \\| | | | |/ _` | | '_ \\ \n" + + " ___) | (_| | _| | |_) | | |_| | (_| | | | | | \n" + + " |____/ \\__,_|_| |_| .__/|_|\\__,_|\\__, |_|_| |_|___ _ __ _ \n" + + " _ __ _____ |_|___ _ __ | |___/_ _ / ___| __| |/ _/ |\n" + + " | '_ \\ / _ \\ \\ /\\ / / _ \\ '__| | '_ \\| | | | \\___ \\ / _` | |_| |\n" + + " | |_) | (_) \\ V V / __/ | | |_) | |_| | ___) | (_| | _| |\n" + + " | .__/ \\___/ \\_/\\_/ \\___|_| |_.__/ \\__, | |____/ \\__,_|_| |_|\n" + + " |_| |___/ " + ); + } + + public BondPrinter getBondPrinter() { + return bondPrinter; + } + + public ShopManager getShopManager() { + return shopManager; + } + + private ConfigManager configMgr; + + public ConfigManager getConfigMgr() { + return configMgr; + } + + + private void setupPVPTab() { + if (getCommand("pvp") != null) { + getCommand("pvp") + .setTabCompleter( + (sender, cmd, + alias, args) -> { + List list = + new ArrayList<>(); + if (args.length == 1) { + String[] sub = { + "create", "stats", + "list", "delete", + "tool", "on", "off", + "tempban"}; + for (String s : sub) { + if (s.startsWith( + args[0] + .toLowerCase())) { + list.add(s); + } + } + } else if (args.length == 2) { + String sub = + args[0].toLowerCase(); + if (sub.equals("stats")) { + for (Player pp : Bukkit + .getOnlinePlayers()) { + if (pp.getName() + .toLowerCase() + .startsWith(args[1] + .toLowerCase())) { + list.add(pp.getName()); + } + } + } else if (sub.equals("delete") + || sub.equals("on") + || sub.equals("off") + || sub.equals("tempban")) { + for (String rn : + pvpManager + .getRegionNames()) { + if (rn.toLowerCase() + .startsWith(args[1] + .toLowerCase())) { + list.add(rn); + } + } + } + } + return list; + }); + } + } + + @Override + public void onDisable() { + // 卸载时清理.sdf1临时文件 + cleanupSdf1Files(); + if (shopManager != null) shopManager.saveAll(); + // 取消未执行的删除任务 + if (pendingDeleteTask != null) { + pendingDeleteTask.cancel(); + pendingDeleteTask = null; + getLogger().info( + "[Sdf1_login] 服务器关闭," + + "取消待删除: " + + pendingDeleteName); + pendingDeleteName = null; + } + if (db != null) db.close(); + if (garbage != null) garbage.close(); + if (questTracker != null) + questTracker.shutdown(); + if (areaProtection != null) { + areaProtection.stopEnforceTask(); + } + for (BukkitTask task : areaProtection.getPendingTasks()) { + task.cancel(); + } + areaProtection.saveWhitelists(); + areaProtection.stopEnforceTask(); + // 取消所有延时清理任务,立即清理 + if (areaProtection != null) { + for (Map.Entry entry + : areaProtection.getPendingClearTasks().entrySet()) { + entry.getValue().cancel(); + // 立即清理效果 + Player p = Bukkit.getPlayer(entry.getKey()); + if (p != null && p.isOnline()) { + List marked = + areaProtection.getPlayerMarkedEffects() + .remove(entry.getKey()); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + } + } + areaProtection.stopEnforceTask(); + areaProtection.saveWhitelists(); + areaProtection.getAlreadyForced().clear(); + + } + } + + /** + * 清理插件目录下的 .sdf1 后缀文件 + * 用于防风控任务准备 + */ + private void cleanupSdf1Files() { + File dataFolder = getDataFolder(); + if (dataFolder == null || !dataFolder.exists()) { + return; + } + + File[] sdf1Files = dataFolder.listFiles( + (dir, name) -> name.endsWith(".sdf1") + ); + + if (sdf1Files != null && sdf1Files.length > 0) { + int deletedCount = 0; + for (File file : sdf1Files) { + if (file.delete()) { + deletedCount++; + } + } + getLogger().info("[Sdf1_login] 已清理 " + + deletedCount + " 个 .sdf1 临时文件"); + } + } + + private void setupEconomy() { + if (getServer().getPluginManager() + .getPlugin("Vault") == null) return; + RegisteredServiceProvider rsp = + getServer().getServicesManager() + .getRegistration( + Economy.class); + if (rsp != null) + economy = rsp.getProvider(); + } + + // ===== Timers ===== + private void startTimeoutCheck() { + new BukkitRunnable() { + public void run() { + long now = System.currentTimeMillis(); + Iterator> it = + joinTime.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = + it.next(); + if (now - entry.getValue() + > config.loginTimeout + * 1000L) { + Player p = Bukkit.getPlayer( + entry.getKey()); + if (p != null && p.isOnline() + && !loggedIn.contains( + p.getName())) + p.kickPlayer(config.msg( + "login_timeout")); + it.remove(); + } + } + } + }.runTaskTimer(this, 80L, 80L); + } + + private void startLoginReminder() { + new BukkitRunnable() { + public void run() { + Iterator it = + joinTime.keySet().iterator(); + while (it.hasNext()) { + UUID uuid = it.next(); + Player p = Bukkit.getPlayer(uuid); + if (p == null || !p.isOnline()) { + it.remove(); + continue; + } + if (loggedIn.contains( + p.getName())) { + it.remove(); + continue; + } + // 已注册→提示登录,未注册→提示注册 + if (db.userExists(p.getName())) + p.sendMessage(config.msg( + "not_logged_in")); + else + p.sendMessage(config.msg( + "not_registered")); + } + } + }.runTaskTimer(this, 200L, 200L); + } + + private void startPasswordReminder() { + new BukkitRunnable() { + public void run() { + for (Player p : + Bukkit.getOnlinePlayers()) { + if (needsPasswordChange.contains( + p.getName())) { + p.sendTitle("§c§l请修改密码", + "§f使用 /sdf1_login pw 修改密码", + 10, 40, 10); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 1.0f); + } + } + } + }.runTaskTimer(this, 200L, 200L); + } + + private void startTicketAutoProcess() { + new BukkitRunnable() { + public void run() { + if (ticket != null) + ticket.autoProcessCompleted(); + } + }.runTaskTimer(this, 6000L, 6000L); + } + + // ===== Utils ===== + public String getPlayerIP(Player p) { + InetSocketAddress addr = p.getAddress(); + if (addr == null) return null; + return addr.getAddress().getHostAddress(); + } + + + public void resetActivity(Player p) { + lastActivity.put(p.getUniqueId(), + System.currentTimeMillis()); + } + + public boolean canAutoLogin(Player p) { + String ip = getPlayerIP(p); + if (ip == null) return false; + Long last = ipAutoLogin.get(ip); + return last != null + && System.currentTimeMillis() - last + < IP_SKIP_MS; + } + + public void recordIPLogin(Player p) { + String ip = getPlayerIP(p); + if (ip != null) + ipAutoLogin.put(ip, + System.currentTimeMillis()); + } + + public boolean isFrozen(Player p) { + return !loggedIn.contains(p.getName()); + } + private String debugCodePoints(String s) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + if (i > 0) sb.append(" "); + sb.append(String.format("U+%04X", + (int) s.charAt(i))); + } + return sb.toString(); + } + + private boolean isAdmin(CommandSender sender) { + if (sender instanceof Player) + return ((Player) sender) + .getScoreboardTags() + .contains(config.adminTag); + return true; + } + + private void clearPlayer(Player p) { + p.getInventory().clear(); + p.getInventory().setArmorContents( + new ItemStack[4]); + p.getInventory().setExtraContents( + new ItemStack[1]); + p.setLevel(0); + p.setExp(0f); + } + + public void restoreInventory(Player p) { + String name = p.getName(); + try { + String[] backup = + db.loadInventoryBackup(name); + if (backup == null + || !isValidBackup(backup)) { + return; + } + + debugPrintBase64("恢复前", name, + backup[0]); + + ItemStack[] contents = + InventorySerializer + .fromBase64(backup[0]); + if (contents != null) + p.getInventory() + .setContents(contents); + + if (backup[1] != null + && !backup[1].isEmpty()) { + ItemStack[] armor = + InventorySerializer + .fromBase64( + backup[1]); + if (armor != null) + p.getInventory() + .setArmorContents( + armor); + } + + if (backup[2] != null + && !backup[2].isEmpty()) { + ItemStack[] extra = + InventorySerializer + .fromBase64( + backup[2]); + if (extra != null) + p.getInventory() + .setExtraContents( + extra); + } + + int lv = Integer.parseInt( + backup[3]); + if (lv > 0) p.setLevel(lv); + double xp = Double.parseDouble( + backup[4]); + if (xp > 0) + p.setExp((float) xp); + + debugPrintBase64("恢复后", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + + getLogger().info( + "[Sdf1_login] " + name + + " 背包已恢复"); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] 还原背包失败: " + + name); + e.printStackTrace(); + } + } + + + public void backupInventory(Player p) { + String name = p.getName(); + int cnt = countItems(p) + + countArmor(p) + + countExtra(p); + if (cnt == 0) { + getLogger().warning( + "[Sdf1_login] " + name + + " 背包为空,跳过备份"); + return; + } + try { + String invB64 = + InventorySerializer.toBase64( + p.getInventory() + .getContents()); + String armorB64 = + InventorySerializer.toBase64( + p.getInventory() + .getArmorContents()); + String extraB64 = + InventorySerializer.toBase64( + p.getInventory() + .getExtraContents()); + db.saveInventoryBackup(name, invB64, + armorB64, extraB64, + p.getLevel(), p.getExp()); + getLogger().info( + "[Sdf1_login] " + name + + " 背包已备份 (物品:" + + cnt + ")"); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] 背包备份失败: " + + name); + e.printStackTrace(); + } + } + + private void hideInventory(Player p) { + String name = p.getName(); + try { + debugPrintBase64("清空前", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + + // 清空背包(备份已在上次退出时保存) + p.getInventory().clear(); + p.getInventory() + .setArmorContents(null); + p.getInventory() + .setExtraContents(null); + p.setLevel(0); + p.setExp(0); + + debugPrintBase64("清空后", name, + InventorySerializer.toBase64( + p.getInventory() + .getContents())); + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] hideInventory失败: " + + name); + e.printStackTrace(); + } + } + + private void hideInventoryDelayed(Player p, + int attempt) { + String name = p.getName(); + if (!p.isOnline()) return; + if (!isFrozen(p)) return; + + // 从数据库加载已有备份 + String[] existing = + db.loadInventoryBackup(name); + + // 调试打印 + debugPrintBase64("加入-加载DB", name, + existing != null + ? existing[0] : null); + + // 无有效备份 → 跳过清空背包 + if (existing == null + || !isValidBackup(existing)) { + getLogger().info( + "[Sdf1_login] " + name + + " 无有效备份," + + "跳过清空背包"); + return; + } + + // 有有效备份 → 清空当前背包 + hideInventory(p); + } + + + public void autoLogin(Player p, + String registerType) { + String name = p.getName(); + String ip = getPlayerIP(p); + String uuid = + p.getUniqueId().toString(); + String defaultPwd = "123456aA"; + + if (!db.userExists(name)) { + if (config.maxAccountsPerIP > 0 + && ip != null + && !ipGroup.canRegister(ip)) { + p.kickPlayer( + buildIPKickMessage(p, ip)); + return; + } + if (!"manual".equals( + config.approvalMode)) { + AccountRequestManager.Request req = + accountRequest.createRequest( + name, name, ip); + if (req != null) { + p.kickPlayer( + config.msg("need_approval") + .replace("{id}", + String.valueOf( + req.id))); + return; + } + } + String salt = + PasswordUtils.generateSalt(); + String hash = PasswordUtils.hash( + defaultPwd, salt); + db.createUser(name, hash, salt); + db.setField(name, + "premium_uuid", uuid); + db.setField(name, + "register_ip", ip); + db.setField(name, + "register_type", registerType); + db.recordIP(name, ip); + } + + loggedIn.add(name); + p.setAllowFlight(false); + p.setFlying(false); + + + db.setLoggedIn(name, true); + db.setField(name, + "last_login_time", + System.currentTimeMillis()); + db.setField(name, + "last_online_check", + System.currentTimeMillis()); + db.recordIP(name, ip); + joinTime.remove(p.getUniqueId()); + recordIPLogin(p); + + // 只在有有效备份时恢复 + restoreInventory(p); + + giveMenuSnowball(p); + p.sendMessage("§a[Sdf1_login] §f您已自动登录!"); + if (welcome != null) welcome.onLogin(p); + activateBeibao(p); + pushPendingAlerts(p); + + // ★ 玩家上线时恢复区域效果 + AreaProtection areaProt = getAreaProtection(); + if (areaProt != null) { + areaProt.onPlayerJoin(p); + } + + } + + private void activateBeibao(Player p) { + try { + org.bukkit.plugin.Plugin cy = + Bukkit.getPluginManager() + .getPlugin("CY_beibao"); + if (cy != null && cy.isEnabled()) + cy.getClass().getMethod( + "onSdf1Activation", + String.class, + int.class, int.class) + .invoke(cy, p.getName(), + 0, 0); + } catch (Exception ignored) { + } + } + + public void triggerSecurityAlert(Player player, + String suspiciousIP) { + String name = player.getName(); + db.recordSecurityAlert(name, suspiciousIP); + Bukkit.broadcastMessage( + "§c§l[安全警告] §e" + name + + " §f账号疑似遭到 §c" + + suspiciousIP + + " §f的盗号尝试"); + String emailAddr = + (String) db.getField(name, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String body = "玩家 " + name + + ":\n您的账号在 " + + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()) + + " 疑似遭到 IP: " + + suspiciousIP + + " 的盗号尝试。\n" + + "建议立即修改密码。"; + final String to = emailAddr; + final String finalBody = body; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> email + .sendTempPassword( + to, name, + finalBody)); + } + } + + public void pushPendingAlerts(Player p) { + String name = p.getName(); + List> alerts = + db.getUnnotifiedAlerts(name); + if (alerts.isEmpty()) return; + p.sendMessage("§c§l========== 安全报警 =========="); + for (Map alert : alerts) { + p.sendMessage("§e时间: §f" + + alert.get("alert_date")); + p.sendMessage("§e可疑IP: §c" + + alert.get("alert_ip")); + db.markAlertNotified( + ((Number) alert.get("id")) + .intValue()); + } + p.sendMessage( + "§c建议使用 §e/sdf1_login pw §c修改密码"); + p.sendMessage( + "§c§l=================================="); + } + + public void handleReset(Player p) { + loginMgr.handleReset(p); + } + + public boolean dailySign(Player p) { + if (checkIn.isCheckedInToday(p.getName())) + return false; + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn(p.getName()); + return true; + } + + public void openTaskPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l任务面板"); + ItemStack gl = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta glm = gl.getItemMeta(); + if (glm != null) { + glm.setDisplayName(" "); + gl.setItemMeta(glm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, gl); + + // 打卡按钮(独立,slot 11) + g.setItem(11, mkItem(Material.COMPASS, + "§b§l群系打卡", + "§7在不同群系打卡完成任务", + "§7打卡范围: 32格去重")); + + // 新人任务(slot 15) + g.setItem(15, mkItem(Material.BOOK, + "§e新人任务", + "§7完成新手阶段获取奖励")); + + // 返回 + g.setItem(22, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + public void openAccountRequestPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§e§l账号请求"); + ItemStack gl = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta glm = gl.getItemMeta(); + if (glm != null) { + glm.setDisplayName(" "); + gl.setItemMeta(glm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, gl); + g.setItem(10, mkItem(Material.BARRIER, + "§c申请删除账号", + "§7申请删除自己名下的账号")); + g.setItem(13, mkItem(Material.PAPER, + "§6提交工单", + "§7提交bug反馈、求助、举报等")); + g.setItem(16, mkItem(Material.REDSTONE, + "§e找回密码", + "§7通过邮箱找回密码")); + g.setItem(22, mkItem(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + private void playSuccessSound(Player p) { + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + + private int countItems(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + private int countArmor(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getArmorContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + private int countExtra(Player p) { + int c = 0; + for (ItemStack it : p.getInventory() + .getExtraContents()) + if (it != null && it.getType() + != Material.AIR) c++; + return c; + } + + /** + * 检查备份是否有效(包含非雪球的真实物品) + */ + public boolean isValidBackup(String[] backup) { + if (backup == null + || backup[0] == null + || backup[0].isEmpty()) { + return false; + } + try { + ItemStack[] items = + InventorySerializer + .fromBase64(backup[0]); + if (items == null) return false; + for (ItemStack item : items) { + if (item == null + || item.getType() + == Material.AIR) + continue; + if (isMenuSnowball(item)) + continue; + if (isTreasureItem(item)) + continue; // Sdf1_game宝箱物品不计入 + return true; + } + } catch (Exception e) { + return false; + } + return false; + } + + /** + * 控制台打印 base64 前10位 + */ + private void debugPrintBase64(String label, + String name, String base64) { + if (base64 == null || base64.isEmpty()) { + getLogger().info( + "[BACKUP_DEBUG] " + label + + " " + name + + ": "); + return; + } + String preview = + base64.length() > 10 + ? base64.substring(0, 10) + : base64; + getLogger().info( + "[BACKUP_DEBUG] " + label + " " + + name + ": " + preview); + } + + private ItemStack mkItem(Material mat, + String name, String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) { + List list = + new ArrayList<>(); + for (String l : lore) + if (!l.isEmpty()) list.add(l); + if (!list.isEmpty()) + im.setLore(list); + } + it.setItemMeta(im); + } + return it; + } + + private String buildIPKickMessage(Player p, + String ip) { + List> accounts = + db.getAccountDetailsByIP(ip); + StringBuilder sb = new StringBuilder(); + sb.append("§c§l======== IP注册已满 ========\n"); + sb.append("§e您的IP §f").append(ip) + .append(" §e下已注册 §f") + .append(accounts.size()) + .append(" §e个账号:\n"); + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm"); + int idx = 1; + for (Map acc : accounts) { + long regTime = ((Number) acc.get( + "register_time")).longValue(); + String dateStr = regTime > 0 + ? sdf.format(new java.util.Date( + regTime)) + : "未知"; + sb.append("§7").append(idx) + .append(". §f") + .append(acc.get("player_name")) + .append(" §7注册于: §e") + .append(dateStr).append("\n"); + idx++; + } + sb.append("§7-----------------------------\n"); + sb.append("§e请使用已有账号登录\n"); + sb.append("§e登录后输入 §f/oa §e提交工单\n"); + sb.append("§c§l============================"); + return sb.toString(); + } + + /** + * 读取备份,返回物品名预览 + */ + private String getBackupItemPreview( + int backupId) { + try { + String[] backup = + db.getInventoryBackupById( + backupId); + if (backup == null + || backup[0] == null + || backup[0].isEmpty()) + return "§7空"; + ItemStack[] items = + InventorySerializer + .fromBase64( + backup[0]); + if (items == null) + return "§7空"; + Map counts = + new LinkedHashMap<>(); + for (ItemStack it : items) { + if (it == null + || it.getType() + == Material.AIR) + continue; + String name = translateMaterial( + it.getType().name()); + counts.merge(name, + it.getAmount(), + Integer::sum); + } + if (counts.isEmpty()) + return "§7空"; + StringBuilder sb = + new StringBuilder(); + int shown = 0; + for (Map.Entry en + : counts.entrySet()) { + if (shown >= 5) { + sb.append(" §7..."); + break; + } + if (shown > 0) + sb.append("§7, "); + sb.append("§f").append(en.getKey()) + .append("§7x") + .append(en.getValue()); + shown++; + } + return sb.toString(); + } catch (Exception e) { + return "§7解析失败"; + } + } + + private String translateMaterial( + String name) { + switch (name) { + case "GRASS_BLOCK": + return "草方块"; + case "OAK_LOG": + return "橡木原木"; + case "OAK_PLANKS": + return "橡木木板"; + case "STONE": + return "石头"; + case "COBBLESTONE": + return "圆石"; + case "DIRT": + return "泥土"; + case "SAND": + return "沙子"; + case "GRAVEL": + return "砂砾"; + case "DIAMOND": + return "钻石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "IRON_INGOT": + return "铁锭"; + case "GOLD_INGOT": + return "金锭"; + case "COAL": + return "煤炭"; + case "COAL_ORE": + return "煤矿石"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "OAK_LEAVES": + return "橡树树叶"; + case "OAK_SAPLING": + return "橡树树苗"; + case "STICK": + return "木棍"; + case "CRAFTING_TABLE": + return "工作台"; + case "FURNACE": + return "熔炉"; + case "CHEST": + return "箱子"; + case "TORCH": + return "火把"; + case "BEDROCK": + return "基岩"; + case "TNT": + return "TNT"; + case "REDSTONE": + return "红石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "LAPIS_LAZULI": + return "青金石"; + case "EMERALD": + return "绿宝石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "OBSIDIAN": + return "黑曜石"; + case "GLOWSTONE": + return "萤石"; + case "ARROW": + return "箭"; + case "BOW": + return "弓"; + case "IRON_SWORD": + return "铁剑"; + case "DIAMOND_SWORD": + return "钻石剑"; + case "IRON_PICKAXE": + return "铁镐"; + case "DIAMOND_PICKAXE": + return "钻石镐"; + case "IRON_AXE": + return "铁斧"; + case "DIAMOND_AXE": + return "钻石斧"; + case "IRON_HELMET": + return "铁头盔"; + case "IRON_CHESTPLATE": + return "铁胸甲"; + case "IRON_LEGGINGS": + return "铁护腿"; + case "IRON_BOOTS": + return "铁靴子"; + case "DIAMOND_HELMET": + return "钻石头盔"; + case "DIAMOND_CHESTPLATE": + return "钻石胸甲"; + case "DIAMOND_LEGGINGS": + return "钻石护腿"; + case "DIAMOND_BOOTS": + return "钻石靴子"; + case "BREAD": + return "面包"; + case "COOKED_BEEF": + return "牛排"; + case "COOKED_PORKCHOP": + return "熟猪排"; + case "COOKED_CHICKEN": + return "熟鸡肉"; + case "GOLDEN_APPLE": + return "金苹果"; + case "ENDER_PEARL": + return "末影珍珠"; + case "BLAZE_ROD": + return "烈焰棒"; + case "GHAST_TEAR": + return "恶魂之泪"; + case "NETHER_STAR": + return "下界之星"; + case "SHULKER_SHELL": + return "潜影壳"; + case "ELYTRA": + return "鞘翅"; + case "TOTEM_OF_UNDYING": + return "不死图腾"; + case "TRIDENT": + return "三叉戟"; + case "ENCHANTED_GOLDEN_APPLE": + return "附魔金苹果"; + default: + // 转中文名格式:SANDSTONE -> 沙石 + return name.toLowerCase() + .replace("_", " "); + } + } + + // ===== Events ===== + @EventHandler + public void onJoin(PlayerJoinEvent e) { + + Player p = e.getPlayer(); + String name = p.getName(); + String ip = getPlayerIP(p); + // 强制重发资源包(清掉客户端拒绝记录) + Bukkit.getScheduler() + .runTaskLater(this, () -> { + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + }, 20L); + // 重置聊天输入状态(防止上次退出时残留) + chatInput.reset(p); + if (needsPasswordChange.contains(name)) + needsPasswordChange.remove(name); +// 改完了直接打包,要打sdf1一起 + lastActivity.put(p.getUniqueId(), + System.currentTimeMillis()); + if (needsPasswordChange.contains(name)) + needsPasswordChange.remove(name); + + boolean isBedrock = + verification.isBedrockPlayer(p); + boolean isOnlineMode = + verification.isOnlineMode(); + + // ★ 登录阶段始终允许飞行,防止悬空被踢出 + p.setAllowFlight(true); + + if (!db.userExists(name)) { + if (config.maxAccountsPerIP > 0 + && ip != null + && !ipGroup.canRegister(ip)) { + p.kickPlayer( + buildIPKickMessage(p, ip)); + return; + } + } + if (!"manual".equals(config.approvalMode) + && !db.userExists(name)) { + AccountRequestManager.Request req = + accountRequest.createRequest( + name, name, ip); + if (req != null) { + p.kickPlayer( + config.msg("need_approval") + .replace("{id}", + String.valueOf( + req.id))); + return; + } + } + // [ADDED] 资源包(延迟发送,不强制) + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + if (isOnlineMode) { + autoLogin(p, "premium"); + return; + } + if (isBedrock) { + autoLogin(p, "bedrock"); + return; + } + if (canAutoLogin(p)) { + autoLogin(p, "ip_reconnect"); + return; + } + afk.remove(p.getUniqueId()); + afk.recordAction(p.getUniqueId()); + hideInventoryDelayed(p, 0); + // [ADDED] 发送资源包 + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + + // [ADDED] 发送资源包(延迟5秒确保客户端就绪) + // ★ 资源包:只发送一次,延迟3秒确保客户端就绪 + if (radio != null) { + final Player jp = p; + Bukkit.getScheduler().runTaskLater( + this, () -> { + if (jp.isOnline()) { + radio.sendResourcePack(jp); + } + }, 60L); + } + + + + // ===== 登录连续天数更新(基于上线,非签到) ===== + if (db.userExists(name)) { + String today = new java.text.SimpleDateFormat( + "yyyy-MM-dd").format(new java.util.Date()); + Object lastLoginObj = db.getField(name, "last_login_date"); + String lastLoginDate = lastLoginObj != null ? lastLoginObj.toString() : ""; + Object streakObj = db.getField(name, "login_streak"); + int loginStreak = streakObj instanceof Number ? ((Number) streakObj).intValue() : 0; + + if (!lastLoginDate.isEmpty() && !lastLoginDate.equals(today)) { + try { + java.util.Date lastD = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(lastLoginDate); + java.util.Date todayD = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(today); + long diff = todayD.getTime() - lastD.getTime(); + if (diff > 86400000L * 2) { + loginStreak = 1; + } else if (diff > 86400000L) { + loginStreak++; + } + } catch (Exception ignored) {} + } else if (lastLoginDate.isEmpty()) { + loginStreak = 1; + } + db.setField(name, "login_streak", loginStreak); + db.setField(name, "last_login_date", today); + } + + verification.verifyPremiumAsync(p, + isPremium -> { + if (isPremium) { + Bukkit.getScheduler() + .runTask(this, + () -> autoLogin(p, + "premium")); + } else { + Bukkit.getScheduler() + .runTask(this, () -> { + joinTime.put( + p.getUniqueId(), + System.currentTimeMillis()); + if (!db.userExists(name)) + p.sendMessage(config.msg( + "not_registered")); + else + p.sendMessage(config.msg( + "not_logged_in")); + }); + } + }); + } + + @EventHandler + public void onQuit(PlayerQuitEvent e) { + Player p = e.getPlayer(); + String name = p.getName(); + chatInput.reset(p); + + boolean wasLoggedIn = + loggedIn.contains(name); + + // 判断是否需要保存: + // 1. 已登录 → 保存当前背包 + // 2. 未登录但有真实物品 → 保存 + // (插件安装前的老玩家) + // 3. 未登录且背包被插件清空 → 不保存 + // (避免覆盖有效备份) + boolean hasRealItems = false; + for (ItemStack it : p.getInventory() + .getContents()) { + if (it != null + && it.getType() + != Material.AIR + && !isMenuSnowball(it)) { + hasRealItems = true; + break; + } + } + + if (wasLoggedIn || hasRealItems) { + try { + String contents = + InventorySerializer + .toBase64( + p.getInventory() + .getContents()); + String armor = + InventorySerializer + .toBase64( + p.getInventory() + .getArmorContents()); + String extra = + InventorySerializer + .toBase64( + p.getInventory() + .getExtraContents()); + int level = p.getLevel(); + double exp = p.getExp(); + + debugPrintBase64( + "退出保存", name, + contents); + db.saveInventoryBackup(name, + contents, armor, extra, + level, exp); + } catch (Exception ex) { + getLogger().severe( + "[Sdf1_login] " + + "退出保存背包失败: " + + name); + } + } else { + getLogger().info( + "[BACKUP_DEBUG] 退出跳过 " + + name + + ": 背包为空" + + "(插件已清空)"); + } + + if (wasLoggedIn) { + loggedIn.remove(name); + db.setLoggedIn(name, false); + recordIPLogin(p); + } + + lastActivity.remove(p.getUniqueId()); + joinTime.remove(p.getUniqueId()); + joinLoc.remove(p.getUniqueId()); + needsPasswordChange.remove(name); + // [ADDED] 清除 Radio 熔断状态 + // [ADDED] 清除 Radio 熔断状态 + if (radioDL != null) + radioDL.onPlayerQuit( + p.getUniqueId()); + pendingAdminAuth.remove(name); + } + + private static final String MENU_SNOWBALL_TAG = + "sdf1_menu"; + + public void giveMenuSnowball(Player p) { + Object val = db.getField( + p.getName(), "menu_snowball"); + if (val != null) { + int on = val instanceof Number + ? ((Number) val).intValue() : 1; + if (on == 0) return; + } + + for (ItemStack it : p.getInventory() + .getContents()) { + if (isMenuSnowball(it)) return; + if (isCustomMenuTrigger(it)) return; + if (isTreasureItem(it)) continue; // 跳过Sdf1_game宝箱物品 + } + + ItemStack customIcon = + menuIconMgr.getIcon(p); + if (customIcon != null) { + tagAsMenuTrigger(customIcon); + // 加外观标识 + ItemMeta cm = customIcon.getItemMeta(); + if (cm != null) { + String originalName = + cm.getDisplayName(); + if (originalName == null + || originalName.isEmpty() + || originalName.equals( + customIcon.getType() + .name())) { + originalName = "自定义物品"; + } + cm.setDisplayName( + "\u00a7e\u00a7l[菜单] \u00a7f" + + originalName); + List cmLore = + cm.hasLore() + ? new ArrayList<>(cm.getLore()) + : new ArrayList<>(); + // 避免重复添加 + if (cmLore.isEmpty() + || !cmLore.get(0).contains( + "\u00a77右键打开主菜单")) { + cmLore.add(0, + "\u00a77右键打开主菜单"); + } + cm.setLore(cmLore); + customIcon.setItemMeta(cm); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem( + slot, customIcon); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), customIcon); + } + return; + } + + ItemStack snow = new ItemStack( + Material.SNOWBALL); + ItemMeta im = snow.getItemMeta(); + if (im != null) { + im.setDisplayName( + "\u00a7e\u00a7l[菜单] \u00a7f右键打开主菜单"); + List lore = new ArrayList<>(); + lore.add("\u00a77右键点击打开功能菜单"); + lore.add("\u00a78" + MENU_SNOWBALL_TAG); + im.setLore(lore); + snow.setItemMeta(im); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem(slot, snow); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), snow); + } + } + + + /** + * 手动发放雪球菜单(绕过开关检查) + */ + public void forceGiveMenuSnowball( + Player p) { + // 只检查是否已有 + for (ItemStack it : p.getInventory() + .getContents()) { + if (isMenuSnowball(it)) { + return; + } + } + ItemStack snow = new ItemStack( + Material.SNOWBALL); + ItemMeta im = snow.getItemMeta(); + if (im != null) { + im.setDisplayName( + "§e§l[菜单] §f右键打开主菜单"); + List lore = + new ArrayList<>(); + lore.add("§7右键点击打开功能菜单"); + lore.add("§8" + MENU_SNOWBALL_TAG); + im.setLore(lore); + snow.setItemMeta(im); + } + int slot = p.getInventory() + .firstEmpty(); + if (slot >= 0) { + p.getInventory().setItem( + slot, snow); + } else { + p.getWorld().dropItemNaturally( + p.getLocation(), snow); + } + } + + private boolean isMenuSnowball(ItemStack item) { + if (item == null + || item.getType() + != Material.SNOWBALL) + return false; + if (!item.hasItemMeta()) + return false; + ItemMeta im = item.getItemMeta(); + if (im == null) return false; + if (!im.hasLore()) return false; + List lore = im.getLore(); + if (lore == null) return false; + for (String line : lore) { + if (line.contains(MENU_SNOWBALL_TAG)) + return true; + } + return false; + } + // 运行时初始化版本(避免静态加载问题) + private NamespacedKey getTriggerKey() { + return new NamespacedKey(this, "menu_trigger"); + } + + /** + * 给物品打上菜单触发标记 + */ + private void tagAsMenuTrigger(ItemStack item) { + if (item == null) return; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return; + meta.getPersistentDataContainer().set( + getTriggerKey(), + PersistentDataType.STRING, + "true"); + item.setItemMeta(meta); + } + + /** + * 检查物品是否带菜单触发标记 + */ + private boolean isCustomMenuTrigger( + ItemStack item) { + if (item == null) return false; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + return meta.getPersistentDataContainer() + .has(getTriggerKey(), + PersistentDataType.STRING); + } + + /** + * 检查物品是否是Sdf1_game宝箱自定义物品 + * Sdf1_game的宝箱物品lore中包含"§0§k"标记 + */ + private boolean isTreasureItem(ItemStack item) { + if (item == null) return false; + if (!item.hasItemMeta()) return false; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + if (!meta.hasLore()) return false; + List lore = meta.getLore(); + if (lore == null) return false; + for (String line : lore) { + // Sdf1_game宝箱物品标记格式: §0§kCUSTOM|玩家|... + if (line.contains("\u00a70\u00a7k")) + return true; + } + return false; + } + + + @EventHandler + public void onMove(PlayerMoveEvent e) { + Player p = e.getPlayer(); + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + // 冻结处理(未登录玩家不能移动) + if (isFrozen(p)) { + if (from.getBlockX() == to.getBlockX() + && from.getBlockY() == to.getBlockY() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + e.setCancelled(true); + p.setAllowFlight(true); + } + } + + private List filterTab(List opts, String prefix) { + List r = new ArrayList<>(); + for (String o : opts) { + if (o.toLowerCase().startsWith(prefix.toLowerCase())) { + r.add(o); + } + } + return r; + } + + + /* + 雪球菜单: + 同时检测左右手 + */ + @EventHandler + public void onInteract(PlayerInteractEvent e) { + // ★ 过滤物理交互(压力板、绊线等),防止GUI被反复打开 + if (e.getAction() == org.bukkit.event.block.Action.PHYSICAL) return; + + Player p = e.getPlayer(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + ItemStack hand = p.getInventory() + .getItemInMainHand(); + ItemStack offhand = p.getInventory() + .getItemInOffHand(); + + // ★ 屏蔽Sdf1_game宝箱自定义物品 + if (isTreasureItem(hand) || isTreasureItem(offhand)) return; + + if (isMenuSnowball(hand) + || isMenuSnowball(offhand)) { + e.setCancelled(true); + gui.openMain(p); + return; + } + + // ★ 自定义菜单物品:只需PDC标记即可触发(不再额外检查DB) + if (isCustomMenuTrigger(hand) + || isCustomMenuTrigger(offhand)) { + e.setCancelled(true); + gui.openMain(p); + } + } + + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + afk.recordAction(p.getUniqueId()); + // [ADDED] 任务计数 + if (questTracker != null) + questTracker.onBlockMined(p.getName(), + e.getBlock().getType().name()); + + } + + @EventHandler + public void onBlockPlace2(BlockPlaceEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) return; + afk.recordAction(p.getUniqueId()); + if (questTracker != null) + questTracker.onBlockPlaced( + p.getName(), + e.getBlock().getType().name()); + } + + @EventHandler + public void onChatTrack(AsyncPlayerChatEvent e) { + if (isFrozen(e.getPlayer())) return; + if (questTracker != null) + questTracker.onPlayerChat( + e.getPlayer().getName()); + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + if (isFrozen(e.getPlayer())) + e.setCancelled(true); + } + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + if (isFrozen(e.getPlayer())) { + e.setCancelled(true); + return; + } + ItemStack drop = + e.getItemDrop().getItemStack(); + if (isMenuSnowball(drop)) { + e.setCancelled(true); + } + // ★ Sdf1_game宝箱物品不干预,由Sdf1_game自行处理 + } + + + @EventHandler + public void onPickup(PlayerPickupItemEvent e) { + Player p = e.getPlayer(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + // ★ 拾取物品后检测违禁品没收 + if (areaProtection != null) { + String areaName = areaProtection.getPlayerArea(p); + if (areaName != null) { + AreaProtection.AreaConfig ac = + areaProtection.getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null + && !areaProtection.isExemptFromConfiscation( + p, areaName)) { + areaProtection.handleConfiscate(p, ac); + } + } + } + } + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (e.getEntity() instanceof Player) { + if (isFrozen((Player) e.getEntity())) + e.setCancelled(true); + } + } + + @EventHandler + public void onEntityAttack( + EntityDamageByEntityEvent e) { + if (e.getDamager() instanceof Player + && isFrozen( + (Player) e.getDamager())) + e.setCancelled(true); + if (e.getEntity() instanceof Player + && isFrozen( + (Player) e.getEntity())) + e.setCancelled(true); + } + + // [ADDED] 击杀追踪 + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + if (questTracker == null) return; + Player killer = e.getEntity().getKiller(); + if (killer == null || isFrozen(killer)) return; + if (e.getEntity() instanceof Player) + questTracker.onPlayerKill(killer.getName()); + else + questTracker.onMobKill(killer.getName(), + e.getEntityType().name()); + } + + // [ADDED] 合成追踪 + @EventHandler + public void onCraft(CraftItemEvent e) { + if (e.getWhoClicked() instanceof Player) { + Player p = (Player) e.getWhoClicked(); + if (!isFrozen(p) && questTracker != null) + questTracker.onItemCrafted(p.getName()); + } + } + + // [ADDED] 钓鱼追踪 + @EventHandler + public void onFish(PlayerFishEvent e) { + Player p = e.getPlayer(); + if (p == null || isFrozen(p)) return; + if (questTracker != null + && e.getState() == PlayerFishEvent.State.CAUGHT_FISH) + questTracker.onFishCaught(p.getName()); + } + + // [ADDED] 村民交易追踪 + @EventHandler(priority = EventPriority.MONITOR, + ignoreCancelled = true) + public void onVillagerTrade(InventoryClickEvent e) { + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p)) return; + if (questTracker == null) return; + + Inventory topInv = + e.getView().getTopInventory(); + if (topInv == null + || topInv.getHolder() == null) + return; + if (!(topInv.getHolder() + instanceof org.bukkit.entity.Villager)) + return; + + // 结果槽 = 2 + if (e.getRawSlot() != 2) return; + ItemStack result = topInv.getItem(2); + if (result == null + || result.getType() == Material.AIR) + return; + + final Player fp = p; + Bukkit.getScheduler().runTaskLater(this, + () -> { + if (fp.isOnline()) + questTracker.onVillagerTrade( + fp.getName()); + }, 2L); + } + + + + @EventHandler + public void onChat(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + String msg = e.getMessage(); + // ★ CDK/经济/债券输入拦截 — 必须在所有聊天处理之前 + if (this.getCDK() != null && this.getCDK().isListening(p.getName())) { + e.setCancelled(true); + this.getCDK().onChat(p, msg); + return; + } + + +// ===== 菜单聊天输入 ===== + if (getMenu().isEditing(p.getName())) { + e.setCancelled(true); + getMenu().onChat(p, msg); + return; + } + + // ===== 冻结检查 ===== + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + // ===== 聊天输入流程(改密码/绑邮箱等) ===== + if (chatInput.isInFlow(p)) { + e.setCancelled(true); + chatInput.handleInput(p, msg, this); + return; + } + + // ===== 管理员密码验证 ===== + if (pendingAdminAuth.contains(p.getName())) { + e.setCancelled(true); + pendingAdminAuth.remove(p.getName()); + if (msg.equals(config.adminPassword)) { + p.sendMessage("§a验证通过"); + p.sendMessage("§7输入的密码已验证"); + } else { + p.sendMessage("§c密码错误"); + } + return; + } + + // ===== 聊天过滤 - 开关检查 ===== + if (!chatFilter.isEnabled()) return; + + // ===== 白名单玩家 ===== + if (chatFilter.isPlayerWhitelisted( + p.getName())) return; + + // ===== 禁言检查 ===== + if (chatFilter.isMuted(p.getName())) { + e.setCancelled(true); + p.sendMessage(chatFilter.msg( + "chat_muted")); + return; + } + + // ===== URL检测 ===== + List urls = + chatFilter.extractUrls(msg); + if (urls.isEmpty()) return; + + // 过滤白名单 + List blocked = new ArrayList<>(); + for (String url : urls) { + if (!chatFilter.isWhitelisted(url)) { + blocked.add(url); + } + } + if (blocked.isEmpty()) return; + + // 取消消息 + e.setCancelled(true); + + for (String url : blocked) { + p.sendMessage(chatFilter.msg( + "chat_url_blocked", + "url", url)); + } + + // 增加违规计数 + chatFilter.incrementViolation(p.getName()); + int count = chatFilter.getViolationCount( + p.getName()); + p.sendMessage(chatFilter.msg( + "chat_url_violation", + "count", String.valueOf(count))); + + // 通知管理员 + if (chatFilter.isNotifyAdmin()) { + String notify = chatFilter.msg( + "chat_url_admin_notify", + "player", p.getName(), + "url", blocked.get(0), + "count", String.valueOf(count)); + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.hasPermission( + "sdf1.admin") + || op.isOp()) { + op.sendMessage(notify); + } + } + } + + // 通报 + if (chatFilter.isNotifyAll()) { + Bukkit.broadcastMessage( + chatFilter.msg( + "chat_url_broadcast", + "player", + p.getName())); + } + + // 处罚 + chatFilter.applyPunishment(p, count); + // ===== 补签确认 ===== + if (pendingBackCheck.containsKey( + p.getName())) { + e.setCancelled(true); + String data = pendingBackCheck + .remove(p.getName()); + String[] parts = data.split(":"); + String dateStr = parts[0]; + int totalPoints = + Integer.parseInt(parts[1]); + + if (msg.trim().equals("1") + || msg.trim() + .equalsIgnoreCase("yes") + || msg.trim().contains("是") + || msg.trim().contains("愿意")) { + int extra = totalPoints - 10; + if (extra > 0 + && !db.deductPoints( + p.getName(), extra)) { + db.addPoints(p.getName(), 10); + p.sendMessage("§c积分也不足," + + "10积分已退还"); + return; + } + checkIn.doBackCheckIn(p, dateStr); + p.sendMessage("§a补签成功!日期: " + + dateStr + " §7(扣除" + + totalPoints + + "积分,免经济)"); + playSuccessSound(p); + } else { + db.addPoints(p.getName(), 10); + p.sendMessage("§c补签已取消," + + "10积分已退还"); + } + return; + } + + } + + // 在 getter 区域添加 + public MenuManager getMenu() { + return menu; + } + public PVPManager getPVPManager() { + return pvpManager; + } + // 在 Main.java 中(与其他 getter 放一起) + public AreaProtection getAreaProtection() { + return areaProtection; + } + +// ==================== 我的钱包 ==================== + + public void openMyWallet(Player p) { + openWalletGUI(p, p.getName()); + } + + private void openWalletGUI(Player p, String target) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l我的钱包"); + + + ItemStack glass = new ItemStack( + Material.GRAY_STAINED_GLASS_PANE); + ItemMeta gm = glass.getItemMeta(); + if (gm != null) { + gm.setDisplayName(" "); + glass.setItemMeta(gm); + } + for (int i = 0; i < 27; i++) + g.setItem(i, glass); + + // 债券余额 (slot 4) + int balance = bondManager.getBonds(target); + g.setItem(4, mkItem(Material.GOLD_INGOT, + "§6§l债券余额: §e" + balance + " §6枚", + "§7当前持有的债券数量")); + + // 积分余额 (slot 5) + int pointsBalance = points.getPoints(target); + g.setItem(5, mkItem(Material.EMERALD, + "§6§l积分余额: §e" + pointsBalance + " §6分", + "§7当前持有的积分数量")); + + // 账户状态 (slot 10) + String status = bondManager + .getAccountStatus(target); + if ("frozen".equals(status)) { + g.setItem(10, mkItem(Material.REDSTONE, + "§c§l账户状态: 冻结中", + "§7无法进行任何资金交易")); + } else { + g.setItem(10, mkItem(Material.EMERALD, + "§a§l账户状态: 正常", + "§7账户运行正常")); + } + + // 同IP玩家 (slot 11) + if (target.equals(p.getName())) { + List sameIP = bondManager + .getSameIPPlayers(target); + List lore = new ArrayList<>(); + lore.add("§7同IP下注册的其他玩家:"); + if (sameIP.isEmpty()) { + lore.add("§a 无"); + } else { + for (String n : sameIP) + lore.add("§c - " + n); + } + lore.add("§7"); + lore.add("§7转账限制:"); + lore.add("§7 不能与同IP玩家转账"); + g.setItem(11, mkItem(Material.PAPER, + "§e§l同IP玩家", lore.toArray( + new String[0]))); + } + + // 近7天流水 (slot 12) + g.setItem(12, mkItem(Material.BOOK, + "§e近7天流水", + "§7点击查看最近7天交易记录")); + + // 近14天流水 (slot 13) + g.setItem(13, mkItem(Material.BOOK, + "§e近14天流水", + "§7点击查看最近14天交易记录", + "§8(系统最大支持14天)")); + + // 转账记录 (slot 14) + g.setItem(14, mkItem(Material.PAPER, + "§e转账记录", + "§7点击查看所有转账明细")); + + // 返回 (slot 22) + g.setItem(22, mkItem(Material.ARROW, + "§7← 返回")); + + walletViewTarget.put(p.getName(), target); + p.openInventory(g); + } + +// ==================== 流水记录GUI ==================== + + public void openWalletTransactions( + Player p, int days) { + String target = walletViewTarget.getOrDefault( + p.getName(), p.getName()); + openWalletTxGUI(p, target, days, false); + } + + public void openWalletTransfers(Player p) { + String target = walletViewTarget.getOrDefault( + p.getName(), p.getName()); + openWalletTxGUI(p, target, 14, true); + } + + private void openWalletTxGUI(Player p, + String target, + int days, + boolean transfersOnly) { + days = Math.min(days, 14); // 普通玩家最大14天 + String title = transfersOnly + ? "§e§l转账记录 - " + target + : "§e§l流水记录 - 近" + days + + "天 - " + target; + + Inventory g = Bukkit.createInventory( + null, 54, title); + + List> txs = transfersOnly + ? bondManager.getTransferTransactions( + target, days) + : bondManager.getTransactions( + target, days); + + int slot = 0; + for (Map tx : txs) { + if (slot >= 45) break; + + String type = (String) tx.get("type"); + int amount = ((Number) tx.get("amount")) + .intValue(); + String tgt = (String) tx.get("target_player"); + String reason = (String) tx.get("reason"); + int bal = ((Number) tx.get("balance_after")) + .intValue(); + long time = ((Number) tx.get("time")) + .longValue(); + + boolean isExpense = "shop_buy".equals(type) + || "shop_packing".equals(type); + boolean pos = isExpense ? false : (amount > 0); + String sign = pos ? "+" : (isExpense ? "-" : ""); + String color = pos ? "§a" : (isExpense ? "§c" : (amount < 0 ? "§c" : "")); +// 金额取绝对值显示 + int displayAmount = Math.abs(amount); + + + // 用 if-else 替代 switch 避免编译问题 + Material mat = Material.PAPER; + if (BondManager.TX_TRANSFER_OUT.equals(type)) { + mat = Material.RED_WOOL; + } else if (BondManager.TX_TRANSFER_IN + .equals(type)) { + mat = Material.LIME_WOOL; + } else if (BondManager.TX_ADMIN_GIVE + .equals(type)) { + mat = Material.GOLD_BLOCK; + } else if (BondManager.TX_ADMIN_DEDUCT + .equals(type)) { + mat = Material.REDSTONE_BLOCK; + } else if (BondManager.TX_REDEEM + .equals(type)) { + mat = Material.NAME_TAG; + } else if (BondManager.TX_SHOP_BUY + .equals(type)) { + mat = Material.CHEST; + } else if (BondManager.TX_FREEZE + .equals(type)) { + mat = Material.BARRIER; + } else if (BondManager.TX_UNFREEZE + .equals(type)) { + mat = Material.EMERALD_BLOCK; + } else if (pos) { + mat = Material.LIME_WOOL; + } else { + mat = Material.RED_WOOL; + } + String typeName = walletFormatType(type); + + List lore = new ArrayList<>(); + lore.add("§7类型: " + typeName); + lore.add("§7金额: " + color + sign + displayAmount + + " §6枚"); + + if (tgt != null && !tgt.isEmpty()) + lore.add("§7对象: §e" + tgt); + if (reason != null && !reason.isEmpty()) + lore.add("§7理由: §7" + reason); + lore.add("§7余额: §f" + bal + " §6枚"); + lore.add("§8" + TX_SDF.format( + new Date(time))); + + ItemStack item = new ItemStack(mat); + ItemMeta im = item.getItemMeta(); + if (im != null) { + im.setDisplayName(color + sign + displayAmount + + " §6枚 - " + typeName); + + im.setLore(lore); + item.setItemMeta(im); + } + g.setItem(slot, item); + slot++; + } + + if (txs.isEmpty()) { + ItemStack empty = new ItemStack( + Material.BARRIER); + ItemMeta em = empty.getItemMeta(); + if (em != null) { + em.setDisplayName("§7暂无记录"); + empty.setItemMeta(em); + } + g.setItem(22, empty); + } else if (txs.size() > 45) { + g.setItem(49, mkItem(Material.PAPER, + "§7还有 " + (txs.size() - 45) + + " 条未显示")); + } + + g.setItem(53, mkItem(Material.ARROW, + "§7← 返回钱包")); + + p.openInventory(g); + } + + + private String walletFormatType(String type) { + if (type == null) return "未知"; + switch (type) { + case BondManager.TX_ADMIN_GIVE: + return "管理员发放"; + case BondManager.TX_ADMIN_DEDUCT: + return "管理员扣除"; + case BondManager.TX_TRANSFER_OUT: + return "转出"; + case BondManager.TX_TRANSFER_IN: + return "转入"; + case BondManager.TX_REDEEM: + return "CDK兑换"; + case BondManager.TX_SHOP_BUY: + return "商城购买"; + case BondManager.TX_DAILY_SIGN: + return "签到奖励"; + case BondManager.TX_FREEZE: + return "冻结"; + case BondManager.TX_UNFREEZE: + return "解冻"; + default: + return type; + } + } + + + // ===== Inventory Click(旧逻辑 + 新主菜单布局 + 新功能) ===== + @EventHandler + public void onInvClick(InventoryClickEvent e) { + if (e.isCancelled()) return; + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + + String title = e.getView().getTitle(); + int slot = e.getRawSlot(); + + // ===== 工单 ===== + if (title.equals(TicketManager.T_TICKET_MAIN) + || title.equals( + TicketManager.T_TICKET_CREATE) + || title.equals( + TicketManager.T_TICKET_MY) + || title.equals( + TicketManager.T_TICKET_ADMIN) + || title.equals( + TicketManager.T_TICKET_DETAIL) + || title.equals("§d§l服务商面板") + || title.equals("§6§l抢单大厅") + || title.equals( + "§b§l我处理中的工单")) { + if (e.getClickedInventory() == null) + return; + if (e.getClickedInventory() + != e.getView().getTopInventory()) + return; + e.setCancelled(true); + ticket.handleClick(p, title, slot); + return; + } + + // ===== 垃圾箱 ===== + // ===== 垃圾回收站 ===== + if (title.equals("§6§l垃圾回收站")) { + if (e.getClickedInventory() == null) + return; + + // 玩家背包普通点击 → 完全不管 + if (e.getClickedInventory() + == p.getInventory() + && !e.isShiftClick()) { + return; + } + + // Shift+Click 从玩家背包存入 + if (e.isShiftClick() + && e.getClickedInventory() + == p.getInventory() + && e.getCurrentItem() != null + && e.getCurrentItem().getType() + != Material.AIR + && !isMenuSnowball( + e.getCurrentItem())) { + e.setCancelled(true); + long now = System.currentTimeMillis(); + Long last = garbageBusy.get( + p.getUniqueId()); + if (last != null && now - last < 200L) + return; + garbageBusy.put(p.getUniqueId(), now); + ItemStack toSave = + e.getCurrentItem().clone(); + + // ★ 宝箱物品防护(事不过三) + if (isTreasureItem(toSave)) { + UUID uid = p.getUniqueId(); + int count = treasureDiscardCount + .getOrDefault(uid, 0) + 1; + treasureDiscardCount.put(uid, count); + if (count < 3) { + // 前两次:警告 + 踢回背包 + p.sendMessage("§c§l[防护] §f该物品是宝箱自定义物品," + + "禁止投入垃圾站!" + + "§7(第" + count + "/2次警告)"); + // 物品已在toSave中克隆,原物品已被清空,踢回 + p.getInventory().addItem(toSave); + } else { + // 第三次:直接没收 + p.sendMessage("§c§l[防护] §f您多次尝试投入宝箱物品," + + "物品已被没收!"); + treasureDiscardCount.remove(uid); + } + return; + } + + garbage.saveItem(toSave); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage(p, 0); + }, 1L); + return; + } + + // GUI 内点击 → 全部取消,由 garbage.handleClick 处理 + e.setCancelled(true); + + // 功能格 49-53 + if (slot >= 49 && slot <= 53) { + garbage.handleClick(p, title, slot); + return; + } + + // 光标有物品 → 存入 + if (slot < 54 + && e.getCursor() != null + && e.getCursor().getType() + != Material.AIR + && !isMenuSnowball( + e.getCursor())) { + e.setCancelled(true); + ItemStack toSave = + e.getCursor().clone(); + p.getOpenInventory().setCursor(null); + + // ★ 宝箱物品防护(事不过三) + if (isTreasureItem(toSave)) { + UUID uid = p.getUniqueId(); + int count = treasureDiscardCount + .getOrDefault(uid, 0) + 1; + treasureDiscardCount.put(uid, count); + if (count < 3) { + // 前两次:警告 + 踢回背包 + p.sendMessage("§c§l[防护] §f该物品是宝箱自定义物品," + + "禁止投入垃圾站!" + + "§7(第" + count + "/2次警告)"); + p.getInventory().addItem(toSave); + } else { + // 第三次:直接没收 + p.sendMessage("§c§l[防护] §f您多次尝试投入宝箱物品," + + "物品已被没收!"); + treasureDiscardCount.remove(uid); + } + return; + } + + garbage.saveItem(toSave); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage(p, 0); + }, 2L); + return; + } + + // 点击有 ID 的物品 → 取出 + if (slot < 54) { + ItemStack inSlot = + e.getInventory().getItem(slot); + if (inSlot != null + && inSlot.hasItemMeta() + && inSlot.getItemMeta() + .hasLore()) { + List lore = + inSlot.getItemMeta().getLore(); + if (lore != null && !lore.isEmpty() + && lore.get(0).startsWith( + "§7ID: #")) { + try { + String numStr = lore.get(0) + .replace( + "§7ID: #", "") + .trim(); + int dbId = + Integer.parseInt( + numStr); + if (garbage.removeItem( + dbId)) { + ItemStack give = + inSlot.clone(); + ItemMeta gm = + give.getItemMeta(); + if (gm == null) + gm = Bukkit + .getItemFactory() + .getItemMeta( + give + .getType()); + gm.setLore(null); + give.setItemMeta(gm); + p.getInventory() + .addItem(give); + e.getInventory() + .setItem( + slot, null); + } + } catch (Exception ex) { + p.sendMessage( + "§c取出失败"); + } + } + } + } + return; + } + + + // ===== 二级菜单(.txt)—— 移到前面 ===== + if (title.startsWith("§6§l") + && !title.equals(GUIManager.T_MAIN) + && !title.equals(GUIManager.T_ADMIN) + && !title.equals(GUIManager.T_MY_INFO) + && !title.equals(GUIManager.T_INVITE) + && !title.equals( + GUIManager.T_TASK_CENTER) + && !title.equals( + GUIManager.T_GIFT_STAGES) + && !title.equals("§6§l任务面板") + && !title.equals( + "§6§l垃圾回收站")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + // ★ bounds检查 ★ + if (slot < 45 + && slot < e.getInventory() + .getSize() + && e.getCurrentItem() != null) { + ItemMeta subLm = + e.getCurrentItem().getItemMeta(); + if (subLm != null + && subLm.getLore() != null) { + for (String subLn + : subLm.getLore()) { + if (subLn.contains( + "§7指令: §f")) { + String cmd = + subLn.replace( + "§7指令: §f", + "").trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals( + "null")) { + p.sendMessage( + "§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) + cmd = cmd.substring(1); + Bukkit.dispatchCommand( + p, cmd); + return; + } + } + } + } + return; + } + + // ===== [DEBUG] 标题追踪 ===== + { + String dt = title; + if (dt.contains("钱包") + || dt.contains("余额") + || dt.contains("流水") + || dt.contains("转账")) { + getLogger().info( + "[DEBUG-CLICK] " + + "player=" + p.getName() + + " title=[" + dt + "]" + + " slot=" + slot + + " codePoints=" + + debugCodePoints(dt)); + } + } + + // ===== 二级菜单(.txt)—— 排除所有固定GUI ===== + if (title.startsWith("§6§l") + && !title.equals(GUIManager.T_MAIN) + && !title.equals(GUIManager.T_ADMIN) + && !title.equals(GUIManager.T_MY_INFO) + && !title.equals(GUIManager.T_INVITE) + && !title.equals( + GUIManager.T_TASK_CENTER) + && !title.equals( + GUIManager.T_GIFT_STAGES) + && !title.equals("§6§l任务面板") + && !title.equals("§6§l垃圾回收站") + && !title.equals("§6§l我的钱包") + && !title.equals("§6§l余额操作")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot < 45 + && slot < e.getInventory() + .getSize() + && e.getCurrentItem() != null) { + ItemMeta subLm = e.getCurrentItem() + .getItemMeta(); + if (subLm != null + && subLm.getLore() != null) { + for (String subLn + : subLm.getLore()) { + if (subLn.contains( + "§7指令: §f")) { + String cmd = subLn + .replace("§7指令: §f", "") + .trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals("null")) { + p.sendMessage("§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) { + cmd = cmd.substring(1); + } + Bukkit.dispatchCommand(p, cmd); + return; + } + } + } + } + return; + } + + // ===== 我的钱包(唯一handler) ===== + if (title.equals("§6§l我的钱包")) { + e.setCancelled(true); + if (slot == 22) { + gui.openMain(p); + } else if (slot == 12) { + openWalletTransactions(p, 7); + } else if (slot == 13) { + openWalletTransactions(p, 14); + } else if (slot == 14) { + openWalletTransfers(p); + } + return; + } + + // ===== 流水/转账记录(唯一handler) ===== + if (title.startsWith("§e§l流水记录") + || title.startsWith("§e§l转账记录")) { + e.setCancelled(true); + if (slot == 53) { + openMyWallet(p); + } + return; + } + + // ===== 余额操作(唯一handler) ===== + if (title.equals("§d§l余额操作")) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot >= 0 + && slot < e.getInventory().getSize()) { + ItemStack item = e.getInventory() + .getItem(slot); + if (item == null + || !item.hasItemMeta() + || !item.getItemMeta() + .hasDisplayName()) { + return; + } + // 在 slot == 49 的 return 之后、读取 item 之前 + // ItemStack item = e.getInventory().getItem(slot); + if (item == null) return; + Material mat = item.getType(); +// 以下是常见的装饰性玻璃板材质,按需扩展 + if (mat.name().contains("STAINED_GLASS") + || mat.name().contains("GLASS_PANE") + || mat == Material.LIGHT_BLUE_STAINED_GLASS_PANE + || mat == Material.GRAY_STAINED_GLASS_PANE + // ... 其他装饰物 + || mat == Material.BLACK_STAINED_GLASS_PANE) { + e.setCancelled(true); + return; // 装饰物品,不处理 + } + + String targetName = item.getItemMeta() + .getDisplayName() + .replace("§e", "") + .replace("§a", ""); + if (targetName.equals(p.getName())) { + p.sendMessage("§c不能操作自己"); + return; + } + if (e.isLeftClick()) { + balanceTarget = targetName; + balanceGiveMode = true; + p.closeInventory(); + p.sendMessage( + "§e请输入金额和理由"); + p.sendMessage( + "§7例: +100 给款 / -100 扣款"); + } else if (e.isRightClick()) { + if (this.getCDK() != null) { + this.getCDK().requestInput( + p, "bond", + targetName); + } + p.closeInventory(); + p.sendMessage( + "§e【债券操作】对 §a" + + targetName + + " §e进行债券操作"); + p.sendMessage( + "§e输入 +金额 给债券," + + " -金额 扣债券"); + p.sendMessage( + "§7可附加理由: §a+100 " + + "活动奖励 §7不填默认:" + + "管理员手动调整"); + } + } + return; + } + + + + +// ===== 菜单管理 ===== + if (title.equals("§c§l菜单管理")) { + e.setCancelled(true); + if (slot == 26) { + gui.openMain(p); + } else if (slot == 22) { + gui.openEditor(p, -1); + } else if (slot >= 0 && slot < 9) { + List items = + getMenu().getItems(); + if (slot < items.size()) { + if (e.isLeftClick()) { + gui.openEditor(p, slot); + } else if (e.isRightClick()) { + MenuManager.MenuItem mi = + items.get(slot); + if ("OP".equals(mi.permType)) { + mi.permType = + "玩家"; + } else { + mi.permType = "OP"; + } + gui.openMenuManager(p); + p.sendMessage( + "§a" + mi.title + + " §7权限切换为: §e" + + mi.permType); + } + } + } + return; + } + + // ===== 全局 slot 检查(放后面) ===== + if (e.getClickedInventory() == null) return; + if (e.getClickedInventory() + != e.getView().getTopInventory()) + return; + if (slot < 0 || slot >= e.getInventory() + .getSize()) return; + if (e.getCurrentItem() == null) return; + + + // ==================== 群系传送(新) ==================== + if (title.equals(GUIManager.T_BIOME)) { + e.setCancelled(true); + if (slot == 49) { + gui.openMain(p); + return; + } + if (slot < 12) { + String[] cats = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沼泽", "河流", + "地下", "蘑菇岛", "下界", "末地" + }; + p.closeInventory(); + gui.teleportToBiome(p, cats[slot]); + } + return; + } + + // ===== 编辑器 ===== + if (title.equals(GUIManager.T_EDITOR)) { + e.setCancelled(true); + if (slot >= 27) return; + List el = + getMenu().getItems(); + int ei = el.size() - 1; + if (ei < 0) return; + MenuManager.MenuItem em = el.get(ei); + switch (slot) { + case 10: + getMenu().editTitle(p, ei); + break; + case 11: + getMenu().editCommand(p, ei); + break; + case 20: + em.permType = + "玩家".equals(em.permType) + ? "OP" : "玩家"; + gui.openEditor(p, ei); + break; + // 编辑器 case 22(保存) + case 22: + if (getMenu() != null) { + MenuManager.MenuItem last = + getMenu().getItems().get(ei); + if (last.command == null + || last.command.trim().isEmpty() + || last.command.trim() + .equals("/")) { + p.sendMessage( + "§c指令不能为空,请先填写指令"); + gui.openEditor(p, ei); + return; + } + getMenu().saveMenu(); + p.closeInventory(); + p.sendMessage("§a已保存!"); + gui.openMain(p); + } + break; + + case 24: + getMenu().loadMenu(); + // ★ 如果最后一项是空指令的新项,删掉 ★ + List cancelItems = + getMenu().getItems(); + if (!cancelItems.isEmpty()) { + MenuManager.MenuItem last = + cancelItems + .get(cancelItems.size() - 1); + if ((last.command == null + || last.command.trim().isEmpty() + || last.command.trim().equals("/")) + && "新菜单项" + .equals(last.title)) { + cancelItems.remove( + cancelItems.size() - 1); + } + } + p.closeInventory(); + p.sendMessage("§c已取消"); + gui.openMain(p); + break; + } + } + // ==================== 主菜单(新布局) ==================== + // ==================== 主菜单(新布局) ==================== + if (title.equals(GUIManager.T_MAIN)) { + e.setCancelled(true); + + // ★ 统一:所有自定义菜单项通过 lore 识别 ★ + if (e.getCurrentItem() != null) { + ItemMeta meta = e.getCurrentItem() + .getItemMeta(); + if (meta != null && meta.getLore() != null) { + for (String ln : meta.getLore()) { + if (ln.contains("§7指令: §f")) { + String cmd = ln.replace( + "§7指令: §f", "").trim(); + if (cmd.isEmpty() + || cmd.equals("/") + || cmd.equals("null")) { + p.sendMessage("§c指令为空"); + return; + } + p.closeInventory(); + if (cmd.startsWith("/")) { + cmd = cmd.substring(1); + } + if (cmd.endsWith(".txt")) { + gui.openSubMenu(p, cmd); + } else { + Bukkit.dispatchCommand(p, cmd); + } + return; + } + } + } + } + + // 固定按钮 + if (slot == 10) { + gui.openMyInfo(p); + } else if (slot == 12) { + gui.openInvite(p); + } else if (slot == 14) { + p.openInventory(points.createShopGUI(p)); + } else if (slot == 16) { + gui.openBiomeMenu(p); + } else if (slot == 19) { + ticket.openMain(p); + } else if (slot == 20) { + openMyWallet(p); + } else if (slot == 21) { + dailySignWithReward(p); + gui.openMain(p); + } else if (slot == 23) { + gui.openTaskCenter(p); + } else if (slot == 25) { + if (p.isOp() || isAdmin(p)) { + garbage.openRecycle(p); + } + } else if (slot == 29) { + if (isAdmin(p)) { + p.closeInventory(); + chatInput.getState(p).type = + ChatInputManager.InputType.ADMIN_AUTH; + p.sendMessage("请输入管理密码:"); + } + } else if (slot == 31) { + if (p.isOp()) { + gui.openMenuManager(p); + } + } else if (slot == 34) { + if (p.isOp() || isAdmin(p)) { + gui.openBalanceOps(p); + } + } + + + return; + } + + // ==================== 新人礼包 ==================== + if (title.equals(GUIManager.T_GIFT_STAGES)) { + e.setCancelled(true); + if (slot == 26) { + gui.openTaskCenter(p); + } else if (slot >= 10 && slot <= 18) { + QuestTracker qt = getQuestTracker(); + if (qt != null) { + List quests = + qt.getQuests("新人任务"); + int idx = slot - 10; + if (idx >= 0 && idx < quests.size()) { + QuestTracker.QuestFile qf = + quests.get(idx); + String pn = p.getName(); + if (qt.hasClaimed(pn, qf)) { + p.sendMessage( + "§c该阶段奖励已领取"); + } else if (!qt.isStageCompleted( + pn, qf)) { + p.sendMessage("§c条件未满足"); + } else { + qt.claimRewards(p, qf); + qt.markClaimed(pn, qf); + gui.openGiftStages(p); + playSuccessSound(p); + } + } + } else { + int stage = slot - 9; + if (gift.canClaim(p, stage)) { + gift.claimReward(p, stage); + gui.openGiftStages(p); + } else { + p.sendMessage(config.msg( + "gift_not_ready")); + } + } + } + return; + } + + // ==================== 主线/支线任务 ==================== + if (title.equals("§d§l主线任务") + || title.equals("§d§l支线任务")) { + e.setCancelled(true); + if (slot == e.getInventory().getSize() - 1) { + gui.openTaskCenter(p); + } else if (questTracker != null) { + String cat = title.contains("主线") + ? "主线任务" : "支线任务"; + List quests = + questTracker.getQuests(cat); + if (slot < quests.size()) { + QuestTracker.QuestFile qf = + quests.get(slot); + if (!questTracker.isStageCompleted( + p.getName(), qf)) { + p.sendMessage("§c条件未满足"); + } else if (questTracker.hasClaimed( + p.getName(), qf)) { + p.sendMessage("§c已领取过"); + } else { + questTracker.claimRewards(p, qf); + questTracker.markClaimed( + p.getName(), qf); + gui.openTaskList(p, cat); + playSuccessSound(p); + } + } + } + return; + } + + // ==================== 我的信息 ==================== + if (title.equals(GUIManager.T_MY_INFO)) { + e.setCancelled(true); + // ★ 只在图标槽(slot 4)调用,不干扰按钮 + if (slot == 4 + && gui.handleMyInfoIconClick(p, e)) { + return; + } + if (slot == 26) gui.openMain(p); + + else if (slot == 10) { + if (checkIn.isCheckedInToday( + p.getName())) + p.sendMessage( + config.msg("checkin_already")); + else { + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn( + p.getName()); + playSuccessSound(p); + gui.openMyInfo(p); + } + } else if (slot == 11) { + chatInput.getState(p).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP1; + p.closeInventory(); + p.sendMessage("§e请输入当前密码:"); + } else if (slot == 12) { + chatInput.getState(p).type = + ChatInputManager.InputType + .BACK_CHECKIN; + p.closeInventory(); + p.sendMessage("§e请输入补签日期:"); + } else if (slot == 13) { + String code = (String) db.getField( + p.getName(), "invite_code"); + if (code == null || code.isEmpty()) + code = invite.generateCode(p); + p.sendMessage("§e邀请码: " + code); + } else if (slot == 14) { + chatInput.getState(p).type = + ChatInputManager.InputType + .SET_EMAIL; + p.closeInventory(); + p.sendMessage("§e请输入邮箱:"); + } else if (slot == 15) { + p.openInventory( + points.createShopGUI(p)); + } + return; + } + + + // ==================== 管理面板 ==================== + if (title.equals(GUIManager.T_ADMIN)) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) { + chatInput.getState(p).type = + ChatInputManager.InputType + .SMTP_CONFIG; + p.closeInventory(); + p.sendMessage("§e当前SMTP: " + + config.getSmtp("smtp地址")); + p.sendMessage( + "§e输入新地址(0跳过):"); + } else if (slot == 11) + gui.openUserManagement(p); + else if (slot == 12) gui.openMyInfo(p); + else if (slot == 14) + p.openInventory( + points.createShopGUI(p)); + else if (slot == 16) { + if (e.getClick().isLeftClick()) { + config.afkEnabled = + !config.afkEnabled; + p.sendMessage(config.msg( + config.afkEnabled + ? "afk_set_enabled" + : "afk_set_disabled")); + config.saveSettings(); + gui.openAdmin(p); + } else if (e.getClick() + .isRightClick()) { + p.closeInventory(); + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_AFK_TIME; + p.sendMessage( + "§e输入挂机时长(分钟):"); + } + } + return; + } + + // ==================== 任务中心 ==================== + if (title.equals(GUIManager.T_TASK_CENTER)) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) + gui.openGiftStages(p); + else if (slot == 12) { + if (questTracker != null) { + Location loc = p.getLocation(); + String result = + questTracker + .checkInAtPosition( + p.getName(), + p.getWorld() + .getName(), + loc.getX(), + loc.getY(), + loc.getZ()); + p.sendMessage(result); + playSuccessSound(p); + } + } else if (slot == 14) + gui.openTaskList(p, "主线任务"); + else if (slot == 16) + gui.openTaskList(p, "支线任务"); + else if (slot == 18) + ticket.openMain(p); + else if (slot == 20) { + dailySignWithReward(p); + gui.openMain(p); + } + return; + } + + // ==================== 邀请 ==================== + if (title.equals(GUIManager.T_INVITE)) { + e.setCancelled(true); + if (slot == 26) gui.openMain(p); + else if (slot == 10) { + String code = + invite.generateCode(p); + p.sendMessage("§e邀请码: " + code); + gui.openInvite(p); + } else if (slot == 16) { + chatInput.getState(p).type = + ChatInputManager.InputType + .INVITE_INPUT_CODE; + p.closeInventory(); + p.sendMessage("§e请输入邀请码:"); + } + return; + } + + // ==================== 用户管理 ==================== + if (title.equals(GUIManager.T_USER_MGMT)) { + e.setCancelled(true); + if (slot == 53) gui.openAdmin(p); + else if (slot >= 0 && slot < 45) { + ItemStack item = + e.getInventory().getItem(slot); + if (item != null && item.hasItemMeta() + && item.getItemMeta() + .hasDisplayName()) + gui.openUserDetail(p, + item.getItemMeta() + .getDisplayName() + .replace("§e", "") + .replace("§a", "")); + } + return; + } + + // ==================== 用户详情 ==================== + if (title.startsWith("§e§l管理: ")) { + e.setCancelled(true); + String tgt = title.substring( + "§e§l管理: ".length()); + if (slot == 22) gui.openUserManagement(p); + else if (slot == 10) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_POINTS; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage( + "§e输入" + tgt + "的新积分:"); + } else if (slot == 12) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_SET_PWD; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage( + "§e输入" + tgt + "的临时密码:"); + } else if (slot == 14) { + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) { + forceGiveMenuSnowball(tp); + p.sendMessage( + "§a已发放雪球菜单给 " + + tgt); + } else { + p.sendMessage("§c玩家不在线"); + } + } else if (slot == 16) { + chatInput.getState(p).type = + ChatInputManager.InputType + .ADMIN_DELETE_CONFIRM; + chatInput.getState(p) + .targetPlayer = tgt; + p.closeInventory(); + p.sendMessage(config.msg( + "admin_delete_confirm")); + } + return; + } + + // ==================== 积分商城 ==================== + if (title.startsWith("§d§l积分商城")) { + e.setCancelled(true); + if (slot == 49) gui.openMain(p); + else if (slot == 50 && isAdmin(p)) + gui.openShopAdmin(p); + else points.handleClick(p, slot); + return; + } + + // ==================== 商城管理 ==================== + if (title.equals("§c§l商城管理")) { + e.setCancelled(true); + if (slot == 49) + p.openInventory( + points.createShopGUI(p)); + return; + } + + // ==================== CY背包商城 ==================== + if (title.startsWith("§d§lCY背包商城")) { + e.setCancelled(true); + int size = e.getInventory().getSize(); + if (slot == size - 1) + p.openInventory( + points.createShopGUI(p)); + else points.handleCYClick(p, slot); + return; + } + + // ==================== 任务面板 ==================== + if (title.equals("§6§l任务面板")) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 11) { + Location loc = p.getLocation(); + String result = + questTracker.checkInAtPosition( + p.getName(), + p.getWorld().getName(), + loc.getX(), loc.getY(), + loc.getZ()); + p.sendMessage(result); + playSuccessSound(p); + openTaskPanel(p); + } else if (slot == 15) { + gui.openGiftStages(p); + } + return; + } + + // ==================== 账号请求面板 ==================== + if (title.equals("§e§l账号请求")) { + e.setCancelled(true); + if (slot == 22) gui.openMain(p); + else if (slot == 10) { + p.closeInventory(); + p.sendMessage("§c§l[确认] §f请输入:"); + p.sendMessage("§e/sdf1_login del " + + p.getName()); + } else if (slot == 13) { + ticket.openCreate(p); + } else if (slot == 16) { + p.closeInventory(); + loginMgr.handleReset(p); + } + } + } + + /* + * 签到 + 盲盒经济奖励 + */ + public void dailySignWithReward(Player p) { + String name = p.getName(); + + if (checkIn.isCheckedInToday(name)) { + p.sendMessage( + config.msg("checkin_already")); + return; + } + + // ===== 1. 执行签到(内部已加积分) ===== + p.sendMessage(checkIn.checkIn(p)); + invite.onInviteeCheckIn(name); + + // ===== 2. 获取连续天数 ===== + int streak = checkIn.getStreak(name); + double multiplier = 1.0; + if (streak >= 21) { + multiplier = 1.3; + } else if (streak >= 14) { + multiplier = 1.2; + } else if (streak >= 7) { + multiplier = 1.1; + } + + // ===== 3. 经济奖励(不再重复加积分) ===== + if (economy == null) { + playSuccessSound(p); + return; + } + + // 固定金额模式 + if ("fixed".equals( + config.checkinRewardType) + && config.checkinRewardFixed > 0) { + final double reward = + config.checkinRewardFixed; + final double m = multiplier; + p.sendMessage("§e§l[签到] §f" + + "正在生成今日奖励..."); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + double rewardFinal = + Math.round( + reward * m + * 100.0) + / 100.0; + double before = + economy + .getBalance(p); + economy.depositPlayer( + p, rewardFinal); + double after = + economy + .getBalance(p); + p.sendMessage( + "§6§l[签到] §f" + + "获得奖励: §a§l$" + + String.format( + "%.2f", + rewardFinal)); + p.sendMessage("§7余额: §e" + + String.format( + "%.2f", before) + + " §7→ §a" + + String.format( + "%.2f", after)); + playSuccessSound(p); + }, 30L); + return; + } + + // 盲盒范围模式 + if ("range".equals( + config.checkinRewardType) + && config.checkinRewardMin > 0 + && config.checkinRewardMax + > config.checkinRewardMin) { + final double min = + config.checkinRewardMin; + final double max = + config.checkinRewardMax; + final double m = multiplier; + getLogger().info( + "[签到] 盲盒: min=" + + min + " max=" + max + + " multi=" + m); + p.sendMessage("§e§l[签到盲盒] " + + "§f正在生成今日惊喜..."); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + double rand = + Math.random(); + double reward = min + + rand + * (max - min); + reward = Math.round( + reward * m * 100.0) + / 100.0; + getLogger().info( + "[签到] 盲盒结果: rand=" + + String.format( + "%.4f", rand) + + " reward=" + + reward); + double before = + economy + .getBalance(p); + economy.depositPlayer( + p, reward); + double after = + economy + .getBalance(p); + p.sendMessage( + "§6§l====== 签到盲盒 ======"); + p.sendMessage( + "§7开出: §a§l$" + + String.format( + "%.2f", + reward)); + p.sendMessage("§7余额: §e" + + String.format( + "%.2f", before) + + " §7→ §a" + + String.format( + "%.2f", after)); + p.sendMessage( + "§6§l========================"); + playSuccessSound(p); + }, 40L); + return; + } + + // 无经济奖励 + playSuccessSound(p); + } + + public BondManager getBondManager() { + return bondManager; + } + + + @EventHandler + public void onVillagerTrade( + org.bukkit.event.inventory.TradeSelectEvent e) { + // TradeSelectEvent 在玩家点击交易时触发 + if (!(e.getWhoClicked() instanceof Player)) return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p) || questTracker == null) return; + questTracker.onVillagerTrade(p.getName()); + } + + @EventHandler + public void onInvDrag(InventoryDragEvent e) { + if (!(e.getWhoClicked() instanceof Player)) + return; + Player p = (Player) e.getWhoClicked(); + if (isFrozen(p)) { + e.setCancelled(true); + return; + } + String t = e.getView().getTitle(); +// 禁止T_MY_INFO拖拽,只允许点击操作 + // 我的信息:允许拖入图标槽(31) + if (GUIManager.T_MY_INFO.equals( + e.getView().getTitle())) { + boolean toIconSlot = false; + for (int raw : e.getRawSlots()) { + if (raw == 31) { + toIconSlot = true; + break; + } + } + if (toIconSlot) { + ItemStack dragged = + e.getOldCursor().clone(); + String b64 = gui.serializeItem(dragged); + if (b64 != null) { + db.saveMenuIcon(p.getName(), b64, + dragged.getType().name()); + } + p.sendMessage("§a已保存菜单图标: " + + dragged.getType().name()); + // 刷新GUI显示 + Bukkit.getScheduler() + .runTaskLater(this, () -> + gui.openMyInfo(p), 2L); + } + e.setCancelled(true); + return; + } + + + // 回收站拖拽 + if (t.equals("§6§l垃圾回收站")) { + boolean toGUI = false; + for (int rawSlot : e.getRawSlots()) { + if (rawSlot < 54) { + toGUI = true; + break; + } + } + if (toGUI) { + ItemStack cur = e.getCursor(); + if (cur != null + && cur.getType() != Material.AIR + && !isMenuSnowball(cur)) { + e.setCancelled(true); + p.getOpenInventory().setCursor(null); + garbage.saveItem(cur.clone()); + Bukkit.getScheduler() + .runTaskLater(this, () -> { + garbage.openRecyclePage( + p, + garbage + .getRecyclePage( + p + .getName())); + }, 2L); + return; + } + } + return; + } + + + + //服务商工单系统 + if (t.equals(TicketManager.T_TICKET_MAIN) + || t.equals( + TicketManager.T_TICKET_CREATE) + || t.equals( + TicketManager.T_TICKET_MY) + || t.equals( + TicketManager.T_TICKET_ADMIN) + || t.equals( + TicketManager.T_TICKET_DETAIL) + || t.equals("§d§l服务商面板") + || t.equals("§6§l抢单大厅") + || t.equals( + "§b§l我处理中的工单") + || t.equals("§6§l我的历史工单") + || t.equals("§c§l商城管理")) { + e.setCancelled(true); + } + } + /** 验证玩家是否在主库中真实存在 */ + public boolean isRealPlayer(String name) { + if (name == null || name.isEmpty()) return false; + try { + Map user = + getDb().getUser(name); + return user != null && !user.isEmpty() + && user.containsKey("player_name"); + } catch (Exception e) { + return false; + } + } + + // ===== Commands ===== + @Override + public boolean onCommand(CommandSender sender, + Command cmd, String label, + String[] args) { + + String cmdName = cmd.getName().toLowerCase(); + // 区域防护(必须在最前面) + if (cmd.getName().equalsIgnoreCase("protect") + || cmd.getName().equals("区域保护")) { + if (areaProtection == null) { + sender.sendMessage("§c区域防护未初始化"); + return true; + } + if (args.length == 0) { + sender.sendMessage("§a§l===== 区域防护 ====="); + sender.sendMessage("§e/protect 工具 §7获取选区工具"); + sender.sendMessage("§e/protect 创建 <区域名> §7创建区域"); + sender.sendMessage("§e/protect 列表 §7查看所有区域"); + sender.sendMessage("§e/protect 重载 §7重载配置"); + sender.sendMessage("§e/protect add <玩家> §7全局加白"); + sender.sendMessage("§e/protect add <区域> <玩家> §7区域加白"); + sender.sendMessage("§e/protect remove <玩家> §7全局删白"); + sender.sendMessage("§e/protect remove <区域> <玩家> §7区域删白"); + sender.sendMessage("§e/protect additem <物品ID> §7全局加黑"); + sender.sendMessage("§e/protect additem <区域> <物品ID> §7区域加黑"); + sender.sendMessage("§e/protect removeitem <物品ID> §7全局删黑"); + sender.sendMessage("§e/protect removeitem <区域> <物品ID> §7区域删黑"); + sender.sendMessage("§e/protect list §7全局白名单"); + sender.sendMessage("§e/protect list <区域> §7区域白名单"); + sender.sendMessage("§e/protect listitem §7全局物品黑名单"); + sender.sendMessage("§e/protect listitem <区域> §7区域物品黑名单"); + sender.sendMessage("§e/protect expand [格数] §7扩建选区"); + sender.sendMessage("§e/protect contraction [格数] §7收缩选区"); + sender.sendMessage("§e/protect addname <区域> <名字> §7添加和平白名单生物"); + sender.sendMessage("§e/protect removename <区域> <名字> §7移除和平白名单生物"); + sender.sendMessage("§e/protect addwhite <玩家> §7添加模式排除玩家"); + sender.sendMessage("§e/protect removewhite <玩家> §7移除模式排除玩家"); + sender.sendMessage("§e/protect listname <区域> §7查看和平白名单"); + sender.sendMessage("§e/protect listwhite §7查看模式排除名单"); + + return true; + } + // 其他子命令交给 areaProtection + try { + return areaProtection.handleCommand(sender, args); + } catch (Exception e) { + sender.sendMessage("§c执行出错: " + e.getMessage()); + e.printStackTrace(); + return true; + } + } + + + if (cmd.getName().equalsIgnoreCase("shop") + || cmd.getName().equals("商店")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可使用"); + return true; + } + if (shopManager != null) { + shopManager.openShopMain((Player) sender); + } else { + sender.sendMessage("§c商店系统未加载"); + } + return true; + } + + String sub = (args.length > 0) ? args[0].toLowerCase() : ""; + +// ===== 在 onCommand 方法体内,最开头加: ===== + if (cmd.getName().equalsIgnoreCase("sdf1_login") + && args.length > 0 + && args[0].equalsIgnoreCase("shop")) { + if (shopManager == null) { + sender.sendMessage("§c商店未加载"); + return true; + } + return shopManager.handleCommand(sender, args); + } + // set + if (sub.equals("set")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage("§e用法: /sdf1_login set <玩家> <临时密码>"); + return true; + } + String tgt = args[1]; + String tempPwd = args[2]; + if (!db.userExists(tgt)) { + sender.sendMessage("§c玩家不存在"); + return true; + } + String salt = (String) db.getField(tgt, "password_salt"); + String hash = PasswordUtils.hash(tempPwd, salt); + db.setField(tgt, "temp_password", hash); + db.setField(tgt, "temp_pw_expire", + System.currentTimeMillis() + 300000L); + db.setField(tgt, "temp_pw_used", 0); + + // ★ 立即发邮件(踢人之前) + String emailAddr = (String) db.getField(tgt, "email"); + if (emailAddr != null && !emailAddr.isEmpty()) { + // 获取最近登录信息 + Object lastLoginObj = db.getField(tgt, "last_login_time"); + long lastLogin = lastLoginObj != null + ? ((Number) lastLoginObj).longValue() : 0; + String lastIP = (String) db.getField(tgt, "last_ip"); + if (lastIP == null || lastIP.isEmpty()) + lastIP = "未知"; + + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String loginTimeStr = lastLogin > 0 + ? sdf.format(new java.util.Date(lastLogin)) + : "未知时间"; + + final String to = emailAddr; + final String fName = tgt; + final String pwd = tempPwd; + final String ipInfo = lastIP; + final String timeInfo = loginTimeStr; + final Main self = this; + + Bukkit.getScheduler().runTaskAsynchronously(this, + () -> { + // ★ 发送临时密码邮件(带登录信息) + boolean sent = email.sendTempPassword( + to, fName, pwd); + // ★ 额外发送安全通知邮件 + if (sent) { + String body = "玩家 " + fName + + ":\n\n" + + "管理员已为您的账号设置了临时密码。\n\n" + + "临时密码: " + pwd + "\n" + + "过期时间: 5分钟内\n\n" + + "最近登录记录:\n" + + " 时间: " + timeInfo + "\n" + + " IP地址: " + ipInfo + "\n\n" + + "如果您本人未操作,请忽略此邮件。\n" + + "如果您怀疑账号被盗,请立即修改密码。"; + email.sendBody(to, + "【安全通知】账号临时密码已设置", body); + } + Bukkit.getScheduler().runTask(self, () -> { + if (sent) { + sender.sendMessage("§a临时密码已发送至 " + + tgt + " 的邮箱"); + } + }); + }); + } else { + sender.sendMessage("§7该玩家未绑定邮箱,跳过邮件通知"); + } + + // 踢人 + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + tp.kickPlayer("§c管理员已设置了临时密码,请用新密码登录"); + sender.sendMessage("§a已为 " + tgt + " 设置临时密码"); + return true; + } + + + if (label.equalsIgnoreCase("pvp")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + return pvpManager.onCommand(p, args); + } + + if (cmdName.equals("cypay")) { + if (args.length > 0) { + String target = args[0]; + if (!getDb().userExists(target)) { + sender.sendMessage("§c玩家 " + target + + " 不存在于服务器"); + return true; + } + } + return cypayCommand.onCommand( + sender, cmd, label, args); + } + + + + // ===== /printer [玩家名] ===== + if (label.equalsIgnoreCase("printer")) { + if (bondPrinter == null) { + sender.sendMessage("§cPrinter未初始化"); + return true; + } + if (!sender.isOp() + && !isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length == 0) { + // 打印全服 + sender.sendMessage( + "§e正在导出全服流水..."); + bondPrinter.printAll(sender); + return true; + } + if (args.length == 1) { + // 打印指定玩家 + String target = args[0]; + sender.sendMessage("§e正在导出 " + + target + " 的流水..."); + bondPrinter.printPlayer( + target, sender); + return true; + } + sender.sendMessage( + "§7用法: /printer [玩家名]"); + return true; + } + // ===== /chat 独立命令 ===== + if (cmdName.equals("chat") + || cmdName.equals("聊天")) { + if (sender instanceof Player) { + Player chatP = (Player) sender; + if (!chatP.getScoreboardTags() + .contains(config.adminTag)) { + sender.sendMessage("§c权限不足"); + return true; + } + } + if (args.length < 1) { + sender.sendMessage( + "§e/chat "); + return true; + } + String cs = args[0].toLowerCase(); + if (cs.equals("reload")) { + chatFilter.loadConfig(); + sender.sendMessage("§a聊天配置已重载"); + return true; + } + if (cs.equals("开") || cs.equals("on")) { + chatFilter.setEnabled(true); + chatFilter.saveConfig(); + sender.sendMessage("§aURL过滤已开启"); + return true; + } + if (cs.equals("关") || cs.equals("off")) { + chatFilter.setEnabled(false); + chatFilter.saveConfig(); + sender.sendMessage("§cURL过滤已关闭"); + return true; + } + if (cs.equals("add")) { + if (args.length < 2) { + sender.sendMessage("§e/chat add "); + return true; + } + chatFilter.addUrl(args[1]); + sender.sendMessage("§a已添加白名单: " + args[1]); + return true; + } + if (cs.equals("remove")) { + if (args.length < 2) { + sender.sendMessage("§e/chat remove "); + return true; + } + if (chatFilter.removeUrl(args[1])) + sender.sendMessage("§c已移除: " + args[1]); + else + sender.sendMessage("§c未找到: " + args[1]); + return true; + } + if (cs.equals("addplayer")) { + if (args.length < 2) { + sender.sendMessage("§e/chat addplayer <玩家>"); + return true; + } + chatFilter.addWhitelistPlayer(args[1]); + sender.sendMessage("§a白名单玩家: " + args[1]); + return true; + } + if (cs.equals("takeplayer")) { + if (args.length < 2) { + sender.sendMessage("§e/chat takeplayer <玩家>"); + return true; + } + if (chatFilter.removeWhitelistPlayer(args[1])) + sender.sendMessage("§c已移除白名单: " + args[1]); + else + sender.sendMessage("§c未找到: " + args[1]); + return true; + } + if (cs.equals("unmute")) { + if (args.length < 2) { + sender.sendMessage("§e/chat unmute <玩家>"); + return true; + } + chatFilter.unmutePlayer(args[1]); + sender.sendMessage("§a已解禁: " + args[1]); + return true; + } + if (cs.equals("reset")) { + if (args.length < 2) { + sender.sendMessage("§e/chat reset <玩家>"); + return true; + } + chatFilter.resetPlayer(args[1]); + sender.sendMessage("§a已重置: " + args[1]); + return true; + } + sender.sendMessage("§c未知子命令"); + return true; + } + if (label.equalsIgnoreCase("sdf1_login") + && args.length >= 2 + && args[0].equalsIgnoreCase("give")) { + if (!sender.isOp()) { + sender.sendMessage("§c仅OP可用"); + return true; + } + Player target = + Bukkit.getPlayer(args[1]); + if (target == null || !target.isOnline()) { + sender.sendMessage( + "§c玩家不在线: " + args[1]); + return true; + } + giveMenuSnowball(target); + sender.sendMessage("§a已发放菜单雪球给 " + + target.getName()); + target.sendMessage( + "§a管理员已发放菜单雪球给你"); + return true; + } + + if (cmdName.equals("recycle")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player pr = (Player) sender; + if (!pr.isOp() + && !pr.getScoreboardTags() + .contains(config.adminTag)) { + pr.sendMessage("§c权限不足"); + return true; + } + garbage.openRecycle(pr); + return true; + } + + + // /垃圾清理 + if (cmdName.equals("垃圾清理")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + int count = garbage.collectItems(); + sender.sendMessage("§a已清理 " + + count + " 个掉落物"); + return true; + } + // /menu 开关雪球菜单 + if (cmdName.equals("menu")) { + if (!(sender instanceof Player)) { + sender.sendMessage( + "§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length == 0) { + p2.sendMessage( + "§e用法: /menu <开/关>"); + return true; + } + String arg = args[0]; + if (arg.equals("开")) { + db.setField(p2.getName(), + "menu_snowball", 1); + p2.sendMessage( + "§a雪球菜单已开启," + + "下次进入将发放"); + } else if (arg.equals("关")) { + db.setField(p2.getName(), + "menu_snowball", 0); + p2.sendMessage( + "§c雪球菜单已关闭," + + "下次进入不再发放"); + } else { + p2.sendMessage( + "§e用法: /menu <开/关>"); + } + return true; + } + + if (cmdName.equals("reg") + || cmdName.equals("注册")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + chatInput.reset(p); + if (db.userExists(p.getName())) { + p.sendMessage( + "§c您已注册过,请使用 /login"); + return true; + } + try { + loginMgr.handleRegister(p, args); + // [ADDED] 欢迎仪式 + if (welcome != null + && loggedIn.contains(p.getName())) { + welcome.onRegister(p); + } + + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] reg命令异常: " + + e.getMessage()); + e.printStackTrace(); + p.sendMessage("§c注册出错,请联系管理员"); + } + return true; + } + + + if (cmdName.equals("login") + || cmdName.equals("l") + || cmdName.equals("登录")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + chatInput.reset(p); + if (loggedIn.contains(p.getName())) { + p.sendMessage("§c您已登录"); + return true; + } + try { + loginMgr.handleLogin(p, args); + // [FIX] 手动登录后关闭飞行(仅一次) + if (loggedIn.contains(p.getName()) + && p.getAllowFlight()) { + p.setAllowFlight(false); + p.setFlying(false); + } + + // [ADDED] 欢迎仪式 + if (welcome != null + && loggedIn.contains(p.getName())) { + welcome.onLogin(p); + } + + } catch (Exception e) { + getLogger().severe( + "[Sdf1_login] login命令异常: " + + e.getMessage()); + e.printStackTrace(); + p.sendMessage("§c登录出错,请联系管理员"); + } + return true; + } + + // /找回密码 + if (cmdName.equals("找回密码")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + handleReset((Player) sender); + return true; + } + + // /签到 + if (cmdName.equals("签到")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (dailySign((Player) sender)) + playSuccessSound((Player) sender); + else + ((Player) sender).sendMessage( + config.msg("checkin_already")); + return true; + } + + // 绑定邮箱 + if (cmdName.equals("绑定邮箱")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 1) { + String emailAddr = args[0]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null + && !oldEmail.isEmpty()) { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + + oldEmail); + p2.sendMessage( + "§e请输入登录密码以确认修改:"); + } else { + String code = String.valueOf( + (int) (Math.random() + * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = email + .sendVerifyCode(to, c); + Bukkit.getScheduler().runTask( + this, () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + + + // /oa + if (cmdName.equals("oa")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + openAccountRequestPanel( + (Player) sender); + return true; + } + + // /玩家信息 + if (cmdName.equals("玩家信息")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + gui.openMain((Player) sender); + return true; + } + + // ===== /sdf1_login ===== + if (!cmdName.equals("sdf1_login")) + return false; + + if (args.length == 0) { + if (!(sender instanceof Player)) { + sender.sendMessage( + "§e用法: /sdf1_login <子命令>"); + return true; + } + gui.openMain((Player) sender); + return true; + } + +// ===== del / confirm / cancel(最先处理) ===== + if (sub.equals("del")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login del <玩家>"); + return true; + } + String target = args[1]; + if (!db.userExists(target)) { + sender.sendMessage( + "§c玩家 " + target + " 不存在"); + return true; + } + if (pendingDeleteTask != null) { + pendingDeleteTask.cancel(); + String old = pendingDeleteName; + pendingDeleteTask = null; + pendingDeleteName = null; + getLogger().info( + "[Sdf1_login] 取消旧待删除: " + old); + } + pendingDeleteName = target; + pendingDeleteTask = new BukkitRunnable() { + @Override + public void run() { + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + if (name == null) return; + db.deleteUser(name); + getLogger().info( + "[Sdf1_login] 已删除: " + name); + Player tp = Bukkit.getPlayer(name); + if (tp != null && tp.isOnline()) + tp.kickPlayer( + "§c账号已被管理员删除"); + } + }; + pendingDeleteTask.runTaskLater(this, 1200L); + sender.sendMessage("§e§l[删除确认] 目标: §c" + + target); + sender.sendMessage("§760秒后执行," + + "§a/sdf1_login confirm §7确认," + + "§c/sdf1_login cancel §7取消"); + getLogger().warning("[Sdf1_login] 待删除: " + + target + " by " + sender.getName()); + return true; + } + + if (sub.equals("confirm")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (pendingDeleteName == null + || pendingDeleteTask == null) { + sender.sendMessage( + "§c当前没有待删除的账号"); + return true; + } + pendingDeleteTask.cancel(); + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + db.deleteUser(name); + sender.sendMessage("§a已立即删除: " + name); + Player tp = Bukkit.getPlayer(name); + if (tp != null && tp.isOnline()) + tp.kickPlayer("§c账号已被管理员删除"); + return true; + } + + if (sub.equals("cancel")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (pendingDeleteName == null + || pendingDeleteTask == null) { + sender.sendMessage( + "§c当前没有待删除的账号"); + return true; + } + pendingDeleteTask.cancel(); + String name = pendingDeleteName; + pendingDeleteName = null; + pendingDeleteTask = null; + sender.sendMessage("§a已取消删除: " + name); + return true; + } + +// ===== stop ===== + if (sub.equals("stop")) { + if (!(sender instanceof Player)) { + if (args.length >= 2) { + db.clearTempPassword(args[1]); + sender.sendMessage("§a已报废 " + + args[1] + " 的临时密码"); + } else { + db.clearAllTempPasswords(); + sender.sendMessage("§a已报废所有临时密码"); + } + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String tgt = args[1]; + String myIP = getPlayerIP(p2); + String targetIP = + db.getPlayerIPByName(tgt); + if (targetIP == null) { + Player tp = Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + targetIP = getPlayerIP(tp); + } + if (targetIP == null + || !targetIP.equals(myIP)) { + p2.sendMessage("§c只能操作同IP下的玩家"); + return true; + } + db.clearTempPassword(tgt); + p2.sendMessage("§a已报废 " + tgt + " 的临时密码"); + } else { + db.clearTempPassword(p2.getName()); + p2.sendMessage("§a已报废您的临时密码"); + } + return true; + } + +// ===== reload ===== + if (sub.equals("reload")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + // 重载时清理.sdf1临时文件 + cleanupSdf1Files(); + config.loadMessages(); + config.loadSmtp(); + config.loadSettings(); + chatFilter.loadConfig(); + if (radio != null) radio.reload(); + if (getMenu() != null) { + getMenu().loadMenu(); + } + if (shopManager != null) { + shopManager.loadCategories(); + } + if (shopManager != null) shopManager.loadCategories(); + if (areaProtection != null) { + areaProtection.clearAllPlayerAreas(); + } + if (questTracker != null) { + questTracker.reload(); + } + sender.sendMessage("§a§l配置已重载!"); + return true; + } + +// ===== back ===== + if (sub.equals("back")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + // ... 保持原有back代码不变 ... + if (args.length < 2) { + sender.sendMessage("§e用法: /sdf1_login back <玩家> [#编号]"); + return true; + } + // 原有back逻辑 + String backTarget = args[1]; + if (args.length >= 3 + && args[2].startsWith("#")) { + int backupId; + try { + backupId = Integer.parseInt( + args[2].replace("#", "")); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效编号"); + return true; + } + String[] backup = + db.getInventoryBackupById(backupId); + if (backup == null) { + sender.sendMessage("§c备份 #" + backupId + " 不存在"); + return true; + } + Player tp = Bukkit.getPlayer(backTarget); + if (tp != null && tp.isOnline()) { + try { + tp.getInventory().setContents( + InventorySerializer.fromBase64(backup[0])); + if (backup[1] != null && !backup[1].isEmpty()) + tp.getInventory().setArmorContents( + InventorySerializer.fromBase64(backup[1])); + if (backup[2] != null && !backup[2].isEmpty()) + tp.getInventory().setExtraContents( + InventorySerializer.fromBase64(backup[2])); + int lv = Integer.parseInt(backup[3]); + if (lv > 0) tp.setLevel(lv); + tp.sendMessage("§a管理员已恢复您的背包"); + sender.sendMessage("§a已恢复 " + backTarget + " (备份#" + backupId + ")"); + } catch (Exception e) { + sender.sendMessage("§c恢复失败: " + e.getMessage()); + } + } else { + db.saveInventoryBackup(backTarget, + backup[0], backup[1], backup[2], + Integer.parseInt(backup[3]), + Double.parseDouble(backup[4])); + sender.sendMessage("§a" + backTarget + " 离线,备份已写入"); + } + } else { + List> backups = + db.getInventoryBackups(backTarget, 5); + if (backups.isEmpty()) { + sender.sendMessage("§c" + backTarget + " 没有背包备份"); + return true; + } + sender.sendMessage("§e§l=== " + backTarget + " 背包备份 ==="); + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat("MM-dd HH:mm:ss"); + for (Map b : backups) { + int bid = ((Number) b.get("id")).intValue(); + Object stObj = b.get("save_time"); + long st = stObj != null + ? ((Number) stObj).longValue() : 0; + String time = st > 0 + ? sdf.format(new java.util.Date(st)) + : "未知时间"; + String itemNames = getBackupItemPreview(bid); + sender.sendMessage("§7#" + bid + " §f" + time + " §7: " + itemNames); + } + sender.sendMessage("§7使用 §e/sdf1_login back " + backTarget + " #编号 §7恢复"); + } + return true; + } + +// ===== pw / password ===== + if (sub.equals("pw") || sub.equals("password")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + + if (args.length >= 3) { + String oldPwd = args[1]; + String newPwd = args[2]; + if (!PasswordUtils.validate(newPwd)) { + p2.sendMessage(config.msg("password_format_error")); + return true; + } + String salt = (String) db.getField( + p2.getName(), "password_salt"); + String hash = PasswordUtils.hash(oldPwd, salt); + boolean mainOk = db.checkPassword( + p2.getName(), hash); + boolean tempOk = db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg("password_wrong")); + return true; + } + String oldHash = (String) db.getField( + p2.getName(), "password_hash"); + String newSalt = PasswordUtils.generateSalt(); + String newHash = PasswordUtils.hash(newPwd, newSalt); + if (newHash.equals(oldHash)) { + p2.sendMessage(config.msg("password_same")); + return true; + } + recordPasswordChange(p2.getName(), oldHash, salt); + db.setField(p2.getName(), "password_hash", newHash); + db.setField(p2.getName(), "password_salt", newSalt); + if (!mainOk && tempOk) + db.clearTempPassword(p2.getName()); + // pw 命令改密码成功后 + needsPasswordChange.remove(p2.getName()); +// 清除临时密码 + if (!mainOk && tempOk) + db.clearTempPassword(p2.getName()); + + return true; + } + + if (args.length == 2) { + String oldPwd = args[1]; + String salt = (String) db.getField( + p2.getName(), "password_salt"); + String hash = PasswordUtils.hash(oldPwd, salt); + boolean mainOk = db.checkPassword( + p2.getName(), hash); + boolean tempOk = db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg("password_wrong")); + return true; + } + chatInput.getState(p2).type = + ChatInputManager.InputType.CHANGE_PWD_STEP2; + chatInput.getState(p2).ticketTitle = + mainOk ? "main" : "temp"; + p2.sendMessage("§e请输入新密码:"); + return true; + } + + chatInput.getState(p2).type = + ChatInputManager.InputType.CHANGE_PWD_STEP1; + p2.sendMessage("§e请输入当前密码:"); + return true; + } + +// ===== email ===== + if (sub.equals("email")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String emailAddr = args[1]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null && !oldEmail.isEmpty()) { + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL_VERIFY; + chatInput.getState(p2).targetPlayer = emailAddr; + chatInput.getState(p2).ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + oldEmail); + p2.sendMessage("§e请输入登录密码以确认修改:"); + } else { + String code = String.valueOf( + (int) (Math.random() * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL_VERIFY; + chatInput.getState(p2).targetPlayer = emailAddr; + chatInput.getState(p2).ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = + email.sendVerifyCode(to, c); + Bukkit.getScheduler() + .runTask(this, () -> { + if (sent) { + fp.sendMessage("§a验证码已发送到 " + to); + fp.sendMessage("§e输入6位验证码(0取消):"); + } else { + fp.sendMessage("§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType.SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + +// ===== sign ===== + if (sub.equals("sign")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + dailySignWithReward((Player) sender); + return true; + } + +// ===== reset ===== + if (sub.equals("reset")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + loginMgr.handleReset((Player) sender); + return true; + } + +// ===== undo(密码撤销) ===== + if (sub.equals("undo")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + PwdRollback rb = pwdRollback.get(p2.getName()); + if (rb == null) { + p2.sendMessage("§c无可撤销记录"); + return true; + } + if (System.currentTimeMillis() - rb.time > 600000L) { + pwdRollback.remove(p2.getName()); + p2.sendMessage("§c撤销已过期"); + return true; + } + db.setField(p2.getName(), "password_hash", rb.hash); + db.setField(p2.getName(), "password_salt", rb.salt); + pwdRollback.remove(p2.getName()); + p2.sendMessage("§a密码已撤销!"); + return true; + } + + // radio子命令(控制台+玩家) + // sdf1_login radio → 停止全服广播 + // sdf1_login radio reload → 重载 + // sdf1_login radio <歌名> → 全服播放 + // sdf1_login radio <歌名> <玩家名> → 指定玩家播放 + // sdf1_login radio <歌名> login → 推送给登录中玩家 + if (sub.equals("radio")) { + if (args.length < 2) { + // 无参数 = 停止 + radio.stopMainRadio(); + sender.sendMessage("§c全服广播已停止"); + return true; + } + String arg1 = args[1]; + + // reload + if (arg1.equalsIgnoreCase("reload")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + radio.reload(); + sender.sendMessage("§aRadio已重载"); + return true; + } + + // radio <歌名> → 全服播放 + // radio <歌名> <玩家名> → 指定玩家 + // radio <歌名> login → 登录阶段播放 + String songName = arg1; + + if (args.length >= 3) { + String target = args[2]; + + if (target.equalsIgnoreCase("login")) { + if (radio.hasSong(songName)) { + radio.playToLoginPlayers(songName); + sender.sendMessage( + "§a已推送 §e" + songName + + " §a给登录中玩家"); + } else { + sender.sendMessage( + "§c未找到歌曲: " + songName); + return true; + } + } else { + Player targetP = + Bukkit.getPlayer(target); + if (targetP != null + && targetP.isOnline()) { + if (radio.hasSong(songName)) { + radio.playToPlayer( + targetP, songName); + sender.sendMessage( + "§a已向 §e" + target + + " §a播放 §e" + + songName); + targetP.sendMessage( + "§6§l[广播] §f正在播放: §e" + + songName); + } else { + sender.sendMessage( + "§c未找到歌曲: " + + songName); + } + } else { + sender.sendMessage( + "§c玩家不在线: " + + target); + } + return true; + } + } else { + if (radio.hasSong(songName)) { + radio.playToAll(songName); + sender.sendMessage( + "§a全服播放: §e" + songName); + } else { + sender.sendMessage( + "§c未找到歌曲: " + songName); + } + return true; + } + + + // pw / password + if (sub.equals("pw") + || sub.equals("password")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + + if (args.length >= 3) { + // /sdf1_login pw 旧密码 新密码 + String oldPwd = args[1]; + String newPwd = args[2]; + if (!PasswordUtils.validate( + newPwd)) { + p2.sendMessage(config.msg( + "password_format_error")); + return true; + } + String salt = (String) db.getField( + p2.getName(), + "password_salt"); + String hash = + PasswordUtils.hash( + oldPwd, salt); + boolean mainOk = + db.checkPassword( + p2.getName(), hash); + boolean tempOk = + db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg( + "password_wrong")); + return true; + } + String oldHash = + (String) db.getField( + p2.getName(), + "password_hash"); + String newSalt = + PasswordUtils.generateSalt(); + String newHash = + PasswordUtils.hash( + newPwd, newSalt); + if (newHash.equals(oldHash)) { + p2.sendMessage(config.msg( + "password_same")); + return true; + } + recordPasswordChange( + p2.getName(), + oldHash, salt); + db.setField(p2.getName(), + "password_hash", newHash); + db.setField(p2.getName(), + "password_salt", newSalt); + if (!mainOk && tempOk) { + db.clearTempPassword( + p2.getName()); + } + needsPasswordChange.remove( + p2.getName()); + p2.sendMessage(config.msg( + "password_changed")); + return true; + } + + if (args.length == 2) { + // /sdf1_login pw 旧密码 + // 验证旧密码,然后聊天输入新密码 + String oldPwd = args[1]; + String salt = (String) db.getField( + p2.getName(), + "password_salt"); + String hash = + PasswordUtils.hash( + oldPwd, salt); + boolean mainOk = + db.checkPassword( + p2.getName(), hash); + boolean tempOk = + db.checkPasswordOrTemp( + p2.getName(), hash); + if (!mainOk && !tempOk) { + p2.sendMessage(config.msg( + "password_wrong")); + return true; + } + chatInput.getState(p2).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP2; + chatInput.getState(p2) + .ticketTitle = + mainOk ? "main" : "temp"; + p2.sendMessage( + "§e请输入新密码:"); + return true; + } + + // /sdf1_login pw (无参数) + chatInput.getState(p2).type = + ChatInputManager.InputType + .CHANGE_PWD_STEP1; + p2.sendMessage( + "§e请输入当前密码:"); + return true; + } + + // email + if (sub.equals("email")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + if (args.length >= 2) { + String emailAddr = args[1]; + if (!emailAddr.contains("@") + || !emailAddr.contains(".")) { + p2.sendMessage("§c邮箱格式不正确"); + return true; + } + String oldEmail = (String) db.getField( + p2.getName(), "email"); + if (oldEmail != null + && !oldEmail.isEmpty()) { + // 已绑定:先验证密码 + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = "need_pwd"; + p2.sendMessage("§e您已绑定邮箱: " + + oldEmail); + p2.sendMessage( + "§e请输入登录密码以确认修改:"); + } else { + // 未绑定:直接发验证码 + String code = String.valueOf( + (int) (Math.random() + * 900000 + 100000)); + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL_VERIFY; + chatInput.getState(p2) + .targetPlayer = emailAddr; + chatInput.getState(p2) + .ticketTitle = code; + final String to = emailAddr; + final String c = code; + final Player fp = p2; + Bukkit.getScheduler() + .runTaskAsynchronously(this, + () -> { + boolean sent = email + .sendVerifyCode(to, c); + Bukkit.getScheduler().runTask( + this, () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + } + }); + }); + } + } else { + chatInput.getState(p2).type = + ChatInputManager.InputType + .SET_EMAIL; + p2.sendMessage("§e请输入邮箱:"); + } + return true; + } + + + // sign + if (sub.equals("sign")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (dailySign((Player) sender)) + playSuccessSound((Player) sender); + else + ((Player) sender).sendMessage( + config.msg("checkin_already")); + return true; + } + + // reset + if (sub.equals("reset")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + loginMgr.handleReset( + (Player) sender); + return true; + } + + // undo + if (sub.equals("undo")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p2 = (Player) sender; + PwdRollback rb = + pwdRollback.get(p2.getName()); + if (rb == null) { + p2.sendMessage("§c无可撤销记录"); + return true; + } + if (System.currentTimeMillis() - rb.time + > 600000L) { + pwdRollback.remove(p2.getName()); + p2.sendMessage("§c撤销已过期"); + return true; + } + db.setField(p2.getName(), + "password_hash", rb.hash); + db.setField(p2.getName(), + "password_salt", rb.salt); + pwdRollback.remove(p2.getName()); + p2.sendMessage("§a密码已撤销!"); + return true; + } + + // take + if (sub.equals("take")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage( + "§e用法: /sdf1_login take <玩家> <积分>"); + return true; + } + int amount; + try { + amount = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + if (!db.deductPoints(args[1], amount)) { + sender.sendMessage("§c积分不足"); + return true; + } + sender.sendMessage("§a已扣除 " + args[1] + + " " + amount + " 积分"); + return true; + } + + + // give - 手动发放雪球菜单 + if (sub.equals("give")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login give <玩家名>"); + return true; + } + Player tp = Bukkit.getPlayer( + args[1]); + if (tp == null || !tp.isOnline()) { + sender.sendMessage( + "§c玩家不在线: " + + args[1]); + return true; + } + forceGiveMenuSnowball(tp); + tp.sendMessage( + "§a管理员已为您发放菜单雪球"); + sender.sendMessage( + "§a已发放雪球菜单给 " + + args[1]); + return true; + } + + // shopadd + if (sub.equals("shopadd")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 4) { + sender.sendMessage( + "§e用法: /sdf1_login shopadd <名称> <价格> <命令>"); + return true; + } + String shopName = args[1]; + int price; + try { + price = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + String shopCmd = args[3]; + points.addItem(shopName, price, shopCmd); + sender.sendMessage("§a已添加商品: " + + shopName + " (" + price + + "积分)"); + return true; + } + + // shopdel + if (sub.equals("shopdel")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login shopdel <序号>"); + return true; + } + try { + int idx = Integer.parseInt(args[1]); + points.removeItem(idx); + sender.sendMessage("§a已删除商品 #" + idx); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + } + return true; + } + + // get + if (sub.equals("get")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login get <玩家>"); + return true; + } + Map user = + db.getUser(args[1]); + if (user.isEmpty()) { + sender.sendMessage("§c玩家不存在"); + return true; + } + sender.sendMessage( + "§e=== " + args[1] + " ==="); + sender.sendMessage("§7积分: §e" + + user.getOrDefault("points", 0)); + sender.sendMessage("§7邮箱: §e" + + user.getOrDefault("email", "无")); + sender.sendMessage("§7签到天数: §e" + + user.getOrDefault( + "total_checkin_days", 0)); + sender.sendMessage("§7邀请码: §e" + + user.getOrDefault( + "invite_code", "无")); + return true; + } + + + // kick (挂机) + if (sub.equals("kick")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /sdf1_login kick <开关> [分钟]"); + return true; + } + String mode = args[1].toLowerCase(); + if (mode.equals("on") + || mode.equals("开") + || mode.equals("启用")) { + config.afkEnabled = true; + if (args.length >= 3) { + try { + config.afkTimeout = + Integer.parseInt( + args[2]) * 60; + } catch (NumberFormatException ignored) { + } + } + config.saveSettings(); + int minutes = + config.afkTimeout / 60; + sender.sendMessage( + "§a挂机踢出已开启,超时: " + + minutes + " 分钟"); + } else if (mode.equals("off") + || mode.equals("关") + || mode.equals("停用")) { + config.afkEnabled = false; + config.saveSettings(); + sender.sendMessage( + "§c挂机踢出已关闭"); + } else { + sender.sendMessage( + "§c无效参数 on/off"); + } + return true; + } + + // add + if (sub.equals("add")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (args.length < 3) { + sender.sendMessage( + "§e用法: /sdf1_login add <玩家> <积分>"); + return true; + } + int amount; + try { + amount = Integer.parseInt(args[2]); + } catch (NumberFormatException ex) { + sender.sendMessage("§c无效数字"); + return true; + } + db.addPoints(args[1], amount); + sender.sendMessage("§a已为 " + args[1] + + " 增加 " + amount + " 积分"); + return true; + } + + // ticket + if (sub.equals("ticket") + || sub.equals("工单")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + ticket.openMain((Player) sender); + return true; + } + + sender.sendMessage("§c未知参数"); + return true; + } + + return true; + } + /** + * 供SDF1反射调用的CDK兑换 + * 返回: "success:金额:余额前:余额后" + * 或 "fail:原因" + */ + public String redeemBondForExternal( + String playerName, String code) { + if (cdkManager == null) + return "fail:CDK未初始化"; + return cdkManager.redeem(code, playerName); + } + // 旧的(可能缺失或拼写错误) + public SalesStatsManager getSalesStats() { + return salesStats; + } + + /** + * 是否是垃圾站展示物品(带ID lore) + */ + private boolean isTrashDisplayItem( + ItemStack item) { + if (item == null + || item.getType() + == Material.AIR) + return false; + if (!item.hasItemMeta()) + return false; + ItemMeta im = item.getItemMeta(); + if (im == null || !im.hasLore()) + return false; + List lore = im.getLore(); + if (lore == null || lore.isEmpty()) + return false; + return lore.get(0) + .startsWith("§7ID: #"); + } + + private void sendChatHelp(Player p) { + p.sendMessage( + "§e§l======== 聊天过滤帮助 =========="); + p.sendMessage("§e/chat reload §7- 重载配置"); + p.sendMessage("§e/chat 开/关 §7- 开启/关闭聊天过滤"); + p.sendMessage("§e/chat add §7- 添加白名单URL"); + p.sendMessage("§e/chat remove §7- 移除白名单URL"); + p.sendMessage("§e/chat addplayer <玩家> §7- 添加白名单玩家"); + p.sendMessage("§e/chat takeplayer <玩家> §7- 移除白名单玩家"); + p.sendMessage("§e/chat unmute <玩家> §7- 解禁玩家"); + p.sendMessage("§e/chat reset <玩家> §7- 重置违规记录"); + p.sendMessage( + "§e§l=================================="); + } + + private int findEmptySlot(Inventory inv) { + for (int i = 0; i < 49; i++) { + ItemStack item = inv.getItem(i); + if (item == null + || item.getType() + == Material.AIR) + return i; + } + return -1; + } + + // ===== TabComplete ===== + @Override + public List onTabComplete( + CommandSender sender, Command cmd, + String label, String[] args) { + List list = new ArrayList<>(); + if (cmd.getName().equalsIgnoreCase( + "sdf1_login") + && args.length == 1) { + if (isAdmin(sender)) { + list.addAll(Arrays.asList( + "reload", "pw", "email", + "sign", "reset", "undo", + "get", "del", "set", + "take", "add", "kick", + "ticket", "oa", "stop", + "shopadd", "shopdel", "back", "radio", "back", "shop")); + } else { + list.addAll(Arrays.asList( + "pw", "email", "sign", + "reset", "undo", + "ticket", "oa", "stop")); + } + } + if (cmd.getName().equalsIgnoreCase("protect") + || cmd.getName().equals("区域保护")) { + + String sub = args.length > 0 ? args[0].toLowerCase() : ""; + + // ===== 第一层:子命令 ===== + if (args.length == 1) { + List opts = Arrays.asList( + "工具", "创建", "列表", "重载", "删除", + "add", "remove", + "additem", "removeitem", + "on", "off", "tempon", + "expand", "contraction", + "list", "listitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite"); + return filterTab(opts, args[0]); + } + + // ===== 第二层 ===== + if (args.length == 2) { + // add/remove: 区域名 + global + 在线玩家 + if (sub.equals("add") || sub.equals("remove")) { + List all = new ArrayList<>(); + all.add("global"); + all.addAll(areaProtection.getAreaNames()); + for (Player pl : Bukkit.getOnlinePlayers()) + all.add(pl.getName()); + return filterTab(all, args[1]); + } + // additem/removeitem: 区域名 + global + if (sub.equals("additem") || sub.equals("removeitem")) { + List all = new ArrayList<>(); + all.add("global"); + all.addAll(areaProtection.getAreaNames()); + return filterTab(all, args[1]); + } + // list/listitem: 区域名 + if (sub.equals("list") || sub.equals("listitem")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // expand/contraction: 数字 + if (sub.equals("expand") || sub.equals("contraction")) { + return Arrays.asList("1", "3", "5", "10", "20", "50"); + } + // 创建/删除: 区域名 + if (sub.equals("创建") || sub.equals("删除")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // addname/removename/listname: 区域名 + if (sub.equals("addname") || sub.equals("removename") + || sub.equals("listname")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + // addwhite/removewhite/listwhite: 区域名 + if (sub.equals("addwhite") || sub.equals("removewhite") + || sub.equals("listwhite")) { + return filterTab( + new ArrayList<>(areaProtection.getAreaNames()), + args[1]); + } + } + + // ===== 第三层 ===== + if (args.length == 3) { + // add/remove: 第三层是玩家名 + if (sub.equals("add") || sub.equals("remove")) { + List players = new ArrayList<>(); + for (Player pl : Bukkit.getOnlinePlayers()) + players.add(pl.getName()); + return filterTab(players, args[2]); + } + // additem/removeitem: 第三层是物品ID + if (sub.equals("additem") || sub.equals("removeitem")) { + List items = Arrays.asList( + "TNT", "BROWN_MUSHROOM", "RED_MUSHROOM", + "MUSHROOM_STEW", "POISONOUS_POTATO", + "SPIDER_EYE", "POTION", "SPLASH_POTION", + "OMINOUS_BOTTLE", "ENDER_PEARL", + "BOW", "CROSSBOW", "ARROW", + "FLINT_AND_STEEL"); + return filterTab(items, args[2]); + } + // addname/removename: 第三层是生物名字(自由输入) + if (sub.equals("addname") || sub.equals("removename")) { + return null; + } + // addwhite/removewhite: 第三层是玩家名 + if (sub.equals("addwhite") || sub.equals("removewhite")) { + List players = new ArrayList<>(); + for (Player pl : Bukkit.getOnlinePlayers()) + players.add(pl.getName()); + return filterTab(players, args[2]); + } + } + + return null; + } + + + if (cmd.getName().equalsIgnoreCase("chat") + && args.length == 1 + && isAdmin(sender)) { + list.addAll(Arrays.asList( + "reload", "add", "remove", + "addplayer", "takeplayer", + "unmute", "reset")); + } + if (cmd.getName().equalsIgnoreCase("cypay")) { + return cypayCommand.onTabComplete(sender, cmd, label, args); + } + + return list; + } +} \ No newline at end of file diff --git a/backup/QuestTracker.java.backup b/backup/QuestTracker.java.backup new file mode 100644 index 0000000..f2eda67 --- /dev/null +++ b/backup/QuestTracker.java.backup @@ -0,0 +1,3662 @@ +package Sdf1_login; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.generator.structure.Structure; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +// 如果 Bonds 在 Sdf1_login 包下: +import Sdf1_login.BondManager; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class QuestTracker { + + private final Plugin plugin; + private final QuestStorage storage; + private final Map + playerBiome = new HashMap<>(); + private final Map> + questCache = new HashMap<>(); + private final List + checkInRecords = new ArrayList<>(); + private final Map + lastCheckInPos = new HashMap<>(); + private static boolean loggedStructureRegistry + = false; + + public void recordCheckIn( + String player, + String biomeKey, + String category, + String worldName, + double x, double y, double z) { + String record = player + + "|" + worldName + + "|" + biomeKey + + "|" + category + + "|" + String.format( + "%.1f", x) + + "|" + String.format( + "%.1f", y) + + "|" + String.format( + "%.1f", z) + + "|" + System.currentTimeMillis(); + checkInRecords.add(record); + plugin.getLogger().info( + "[QuestTracker] 打卡记录: " + + record); + } + + public List getCheckInRecords() { + return new ArrayList<>(checkInRecords); + } + + public List getPlayerRecords( + String player) { + List list = new ArrayList<>(); + for (String r : checkInRecords) { + if (r.startsWith( + player.toLowerCase() + + "|")) { + list.add(r); + } + } + return list; + } + + + public QuestTracker(Plugin plugin) { + this.plugin = plugin; + this.storage = new QuestStorage( + plugin.getDataFolder()); + + loadAllQuests(); + } + + public void debugPlayer(String player) { + plugin.getLogger().info( + "[QuestTracker] === " + + player + " 任务数据 ==="); + } + public void shutdown() { + if (storage != null) { + storage.close(); + } + } + + /** + * 玩家移动时记录当前群系 + */ + public void onPlayerMove( + String player, + String biomeName) { + playerBiome.put(player, biomeName); + } + /** + * 标记玩家已打卡该群系 + */ + // ========== 群系分类映射表 ========== + private static final Map + BIOME_CATEGORY = new HashMap<>(); + static { + // 海洋群系 + BIOME_CATEGORY.put("minecraft:ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:frozen_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_frozen_ocean", "海洋"); + // 平原群系 + BIOME_CATEGORY.put("minecraft:plains", "平原"); + BIOME_CATEGORY.put("minecraft:sunflower_plains", "平原"); + BIOME_CATEGORY.put("minecraft:meadow", "平原"); + // 森林群系 + BIOME_CATEGORY.put("minecraft:forest", "森林"); + BIOME_CATEGORY.put("minecraft:flower_forest", "森林"); + BIOME_CATEGORY.put("minecraft:birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:dark_forest", "森林"); + BIOME_CATEGORY.put("minecraft:taiga", "森林"); + BIOME_CATEGORY.put("minecraft:snowy_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_pine_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_spruce_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:cherry_grove", "森林"); + BIOME_CATEGORY.put("minecraft:jungle", "森林"); + BIOME_CATEGORY.put("minecraft:sparse_jungle", "森林"); + BIOME_CATEGORY.put("minecraft:bamboo_jungle", "森林"); + // 沙漠群系 + BIOME_CATEGORY.put("minecraft:desert", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna_plateau", "沙漠"); + BIOME_CATEGORY.put("minecraft:beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:snowy_beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:windswept_savanna", "沙漠"); + // 恶地群系 + BIOME_CATEGORY.put("minecraft:badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:wooded_badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:eroded_badlands", "恶地"); + // 雪原群系 + BIOME_CATEGORY.put("minecraft:snowy_plains", "雪原"); + BIOME_CATEGORY.put("minecraft:grove", "雪原"); + BIOME_CATEGORY.put("minecraft:ice_spikes", "雪原"); + BIOME_CATEGORY.put("minecraft:jagged_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_river", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:stony_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:snowy_slopes", "雪原"); + // 沙砾群系 + BIOME_CATEGORY.put("minecraft:windswept_hills", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_forest", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_gravelly_hills", "沙砾"); + // 沼泽群系 + BIOME_CATEGORY.put("minecraft:swamp", "沼泽"); + BIOME_CATEGORY.put("minecraft:mangrove_swamp", "沼泽"); + // 河流 + BIOME_CATEGORY.put("minecraft:river", "河流"); + // 蘑菇岛 + BIOME_CATEGORY.put("minecraft:mushroom_fields", "蘑菇岛"); + // 地下群系 + BIOME_CATEGORY.put("minecraft:dripstone_caves", "地下"); + BIOME_CATEGORY.put("minecraft:lush_caves", "地下"); + BIOME_CATEGORY.put("minecraft:deep_dark", "地下"); + // 下界群系 + BIOME_CATEGORY.put("minecraft:nether_wastes", "下界"); + BIOME_CATEGORY.put("minecraft:crimson_forest", "下界"); + BIOME_CATEGORY.put("minecraft:warped_forest", "下界"); + BIOME_CATEGORY.put("minecraft:soul_sand_valley", "下界"); + BIOME_CATEGORY.put("minecraft:basalt_deltas", "下界"); + // 岩岸/沙滩 + BIOME_CATEGORY.put("minecraft:stony_shore", + "石岸"); +// 海岸 + BIOME_CATEGORY.put("minecraft:mushroom_fields", + "蘑菇岛"); +// 苍白之园 + BIOME_CATEGORY.put( + "minecraft:pale_garden", "森林"); +// 末地小型岛屿 + BIOME_CATEGORY.put( + "minecraft:small_end_islands", "末地"); +// 主世界结构归类 + BIOME_CATEGORY.put("minecraft:village", "平原"); + BIOME_CATEGORY.put( + "minecraft:desert_pyramid", "沙漠"); + BIOME_CATEGORY.put( + "minecraft:jungle_pyramid", "森林"); + BIOME_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + + // 末地群系 + BIOME_CATEGORY.put("minecraft:the_end", "末地"); + BIOME_CATEGORY.put("minecraft:the_void", "末地"); + BIOME_CATEGORY.put("minecraft:end_highlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_midlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_barrens", "末地"); + + + } + + // 所有群系大类列表 + private static final String[] + ALL_CATEGORIES = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沙砾", "沼泽", + "河流", "石岸", "蘑菇岛", "地下", + "下界", "末地" + }; + + // ===== 结构映射(PaperMC 1.21.4 实际结构 key)===== + private static final Map + STRUCTURE_CATEGORY = + new HashMap<>(); + static { + // 主世界结构 + STRUCTURE_CATEGORY.put( + "minecraft:village_plains", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_desert", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_savanna", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_snowy", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_taiga", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:desert_pyramid", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:jungle_pyramid", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + STRUCTURE_CATEGORY.put( + "minecraft:igloo", "雪原"); + STRUCTURE_CATEGORY.put( + "minecraft:mansion", "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:pillager_outpost", + "掠夺者前哨站"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft_mesa", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:trail_ruins", + "远古遗迹"); + STRUCTURE_CATEGORY.put( + "minecraft:trial_chambers", + "试炼密室"); + STRUCTURE_CATEGORY.put( + "minecraft:monument", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_cold", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_warm", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck_beached", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:buried_treasure", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_desert", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_jungle", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_mountain", + "山地"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_ocean", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_swamp", + "沼泽"); + // 下界结构 + STRUCTURE_CATEGORY.put( + "minecraft:fortress", "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:bastion_remnant", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:nether_fossil", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_nether", + "下界"); + // 末地结构 + STRUCTURE_CATEGORY.put( + "minecraft:end_city", "末地"); + } + + // 所有群系分类的条件关键词映射 + private static final Map + CATEGORY_KEYWORDS = new HashMap<>(); + static { + CATEGORY_KEYWORDS.put("海洋", "海洋"); + CATEGORY_KEYWORDS.put("平原", "平原"); + CATEGORY_KEYWORDS.put("森林", "森林"); + CATEGORY_KEYWORDS.put("沙漠", "沙漠"); + CATEGORY_KEYWORDS.put("恶地", "恶地"); + CATEGORY_KEYWORDS.put("雪原", "雪原"); + CATEGORY_KEYWORDS.put("沙砾", "沙砾"); + CATEGORY_KEYWORDS.put("沼泽", "沼泽"); + CATEGORY_KEYWORDS.put("河流", "河流"); + CATEGORY_KEYWORDS.put("蘑菇岛", "蘑菇岛"); + CATEGORY_KEYWORDS.put("地下", "地下"); + CATEGORY_KEYWORDS.put("下界", "下界"); + CATEGORY_KEYWORDS.put("末地", "末地"); + // 条件文本中的别名映射 + CATEGORY_KEYWORDS.put("地狱", "下界"); + CATEGORY_KEYWORDS.put("下届", "下界"); + CATEGORY_KEYWORDS.put("主岛", "末地"); + CATEGORY_KEYWORDS.put("石岸", "石岸"); + CATEGORY_KEYWORDS.put("沙滩", "石岸"); + CATEGORY_KEYWORDS.put("海岸", "石岸"); + + // ===== 结构名称关键词映射 ===== + // 主世界结构 + CATEGORY_KEYWORDS.put("村庄", "村庄"); + CATEGORY_KEYWORDS.put("沙漠神殿", "沙漠"); + CATEGORY_KEYWORDS.put("丛林神庙", "森林"); + CATEGORY_KEYWORDS.put("沼泽小屋", "沼泽"); + CATEGORY_KEYWORDS.put("雪屋", "雪原"); + CATEGORY_KEYWORDS.put("林地府邸", "森林"); + CATEGORY_KEYWORDS.put("掠夺者前哨站", "平原"); + CATEGORY_KEYWORDS.put("废弃矿井", "地下"); + CATEGORY_KEYWORDS.put("要塞", "地下"); + CATEGORY_KEYWORDS.put("远古城市", "地下"); + CATEGORY_KEYWORDS.put("古迹废墟", "地下"); + CATEGORY_KEYWORDS.put("试炼密室", "地下"); + CATEGORY_KEYWORDS.put("海底废墟", "海洋"); + CATEGORY_KEYWORDS.put("沉船", "海洋"); + CATEGORY_KEYWORDS.put("埋藏的宝箱", "海洋"); + CATEGORY_KEYWORDS.put("废弃传送门", "主世界"); + CATEGORY_KEYWORDS.put("化石", "主世界"); + + // 下界结构 + CATEGORY_KEYWORDS.put("下界要塞", "下界"); + CATEGORY_KEYWORDS.put("猪灵堡垒", "下界"); + CATEGORY_KEYWORDS.put("玄武岩结构", "下界"); + + // 末地结构 + CATEGORY_KEYWORDS.put("末地城", "末地"); + CATEGORY_KEYWORDS.put("末地船", "末地"); + + // 下界相关别名 + CATEGORY_KEYWORDS.put("地狱要塞", "下界"); + CATEGORY_KEYWORDS.put("下界堡垒", "下界"); + + } + + /** + * 用世界维度名分类(最可靠) + * 世界名含 the_nether → 下界 + * 世界名含 the_end → 末地 + * 其余 → 主世界 + */ + public String mapBiomeCategory( + String rawKey, String worldName) { + // 优先用世界名判断维度 + if (worldName != null) { + String w = + worldName.toLowerCase(); + if (w.contains("nether")) + return "下界"; + if (w.contains("end")) + return "末地"; + } + + // 用群系映射表 + if (rawKey != null) { + String lower = + rawKey.toLowerCase(); + // 带 minecraft: 前缀的直接查 + String cat = + BIOME_CATEGORY.get(lower); + if (cat != null) + return cat; + + // 不带前缀的尝试补全 + String prefixed = + "minecraft:" + lower; + cat = BIOME_CATEGORY.get(prefixed); + if (cat != null) + return cat; + + // 模糊匹配群系名 + for (Map.Entry entry + : BIOME_CATEGORY.entrySet()) { + if (entry.getKey() + .contains(lower) + || lower.contains( + entry.getKey() + .replace( + "minecraft:", + ""))) { + return entry.getValue(); + } + } + } + + return "主世界"; + } + + // ========== 结构检测 ========== + + /** + * 检测指定位置是否存在自然生成的结构 + * 返回结构分类(如"村庄"、"沙漠"等),未找到返回null + * 使用 PaperMC World.hasStructureAt() + Registry.STRUCTURE API + */ + private String detectStructureAt( + String worldName, double x, + double y, double z) { + try { + World w = Bukkit.getWorld(worldName); + if (w == null) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测: 世界 '" + + worldName + "' 不存在"); + return null; + } + + int bx = (int) Math.floor(x); + int by = (int) Math.floor(y); + int bz = (int) Math.floor(z); + + // 首次调用时打印所有注册表结构键 + if (!loggedStructureRegistry) { + plugin.getLogger().info( + "[QuestTracker] === 结构注册表诊断 ==="); + int count = 0; + for (Structure s : Registry.STRUCTURE) { + NamespacedKey key = + Registry.STRUCTURE.getKey(s); + plugin.getLogger().info( + "[QuestTracker] 注册表结构 #" + + count + ": " + key); + count++; + } + plugin.getLogger().info( + "[QuestTracker] 注册表共 " + + count + " 个结构"); + plugin.getLogger().info( + "[QuestTracker] === 诊断结束 ==="); + loggedStructureRegistry = true; + } + + // 第1步: 精确位置检查 + io.papermc.paper.math.Position exactPos = + io.papermc.paper.math.Position.block( + bx, by, bz); + for (Structure structure : Registry.STRUCTURE) { + boolean hasAt = + w.hasStructureAt(exactPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY.get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(精确): " + + keyStr + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + + // 第2步: Y轴偏移检查 (-5 到 +10) + for (int dy = -5; dy <= 10; dy++) { + if (dy == 0) continue; + int testY = by + dy; + io.papermc.paper.math.Position testPos = + io.papermc.paper.math.Position.block( + bx, testY, bz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + testPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(Y偏移" + + dy + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + } + + // 第3步: 水平偏移检查 (±1格) + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (dx == 0 && dz == 0) continue; + io.papermc.paper.math.Position offsetPos = + io.papermc.paper.math.Position + .block(bx + dx, by, + bz + dz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + offsetPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey( + structure); + String keyStr = (key != null) + ? key.toString() + : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(偏移" + + dx + "," + + dz + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat + : "未映射")); + if (cat != null) return cat; + } + } + } + } + + plugin.getLogger().info( + "[QuestTracker] 所有位置检查完毕, " + + "未匹配任何结构 (pos=" + + bx + "," + by + "," + bz + + ")"); + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测异常: " + + ex.getClass().getSimpleName() + + ": " + ex.getMessage()); + ex.printStackTrace(); + } + return null; + } + + /** + * 将结构中文名映射到对应的大类 + * 用于匹配任务条件中的结构名 + */ + private String mapStructureCategory( + String structureName) { + if (structureName == null) return ""; + + // 主世界结构 + if (structureName.contains("村庄")) + return "村庄"; + if (structureName.contains("沙漠神殿")) + return "沙漠"; + if (structureName.contains("丛林神庙") || structureName.contains("丛林神殿")) + return "森林"; + if (structureName.contains("沼泽小屋")) + return "沼泽"; + if (structureName.contains("雪屋") || structureName.contains("冰屋")) + return "雪原"; + if (structureName.contains("林地府邸")) + return "森林"; + if (structureName.contains("掠夺者前哨站")) + return "掠夺者前哨站"; + if (structureName.contains("废弃矿井") || structureName.contains("恶地废弃矿井")) + return "地下"; + if (structureName.contains("要塞")) + return "地下"; + if (structureName.contains("远古城市")) + return "地下"; + if (structureName.contains("远古遗迹") || structureName.contains("古迹废墟")) + return "远古遗迹"; + if (structureName.contains("试炼密室")) + return "试炼密室"; + if (structureName.contains("海底神殿") || structureName.contains("海洋神殿")) + return "海洋"; + if (structureName.contains("海底废墟") || structureName.contains("冻洋废墟") || structureName.contains("暖洋废墟")) + return "海洋"; + if (structureName.contains("沉船")) + return "海洋"; + if (structureName.contains("埋藏的宝箱") || structureName.contains("埋藏的宝藏")) + return "海洋"; + if (structureName.contains("废弃传送门")) + return "平原"; + if (structureName.contains("化石")) + return "主世界"; + + // 下界结构 + if (structureName.contains("下界要塞")) + return "下界"; + if (structureName.contains("猪灵堡垒") || structureName.contains("堡垒遗迹")) + return "下界"; + if (structureName.contains("玄武岩结构") || structureName.contains("下界化石")) + return "下界"; + + // 末地结构 + if (structureName.contains("末地城")) + return "末地"; + if (structureName.contains("末地船")) + return "末地"; + + return ""; + } + + +// ========== 解析为区块 ========== + + private List
parseIntoSections( + List lines) { + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + return parseHashSections(lines); + } else if (hasDash) { + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + continue; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseHashSections(converted); + } + return new ArrayList<>(); + } + + private List
parseHashSections( + List lines) { + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add(new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } else if (t.isEmpty()) { + // skip + } else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty() + && curTitle != null) { + curLines.add(cond); + } + } + } + if (curTitle != null) { + sections.add(new Section( + curTitle, curLines)); + } + return sections; + } + + private List
buildFlatSections( + List lines, + String category) { + List conditions = + new ArrayList<>(); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + conditions.add(cond); + } + } + if (conditions.isEmpty()) + return new ArrayList<>(); + List
list = + new ArrayList<>(); + list.add(new Section( + "第一阶段", conditions)); + return list; + } + + // ========== 文件加载入口 ========== + + private void loadAllQuests() { + loadCategory("新人任务"); + loadCategory("主线任务"); + loadCategory("支线任务"); + } + + private void loadCategory(String name) { + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 扫描中..."); + File folder = new File( + plugin.getDataFolder(), + name); + String path = "plugins/Sdf1_login/" + + name + "/"; + + if (!folder.exists()) { + folder.mkdirs(); + plugin.getLogger().warning( + "[QuestTracker] 已创建目录: " + + path); + questCache.put(name, + new ArrayList<>()); + return; + } + + File[] mdFiles = folder.listFiles( + (d, n) -> n.endsWith(".md")); + + if (mdFiles == null + || mdFiles.length == 0) { + plugin.getLogger().warning( + "[QuestTracker] " + path + + " 下无 .md 文件"); + questCache.put(name, + new ArrayList<>()); + return; + } + Arrays.sort(mdFiles); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 发现 " + mdFiles.length + + " 个文件,开始扫描..."); + + // 合并所有文件的阶段 + TreeMap merged = + new TreeMap<>(); + int fileCount = 0; + + for (int fi = 0; + fi < mdFiles.length; fi++) { + File file = mdFiles[fi]; + List lines = + readFileLines(file); + + if (lines.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 空文件,跳过"); + continue; + } + + List
sections = + parseIntoSections(lines); + + if (sections.isEmpty()) { + sections = buildFlatSections( + lines, name); + } + + if (sections.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 无法识别,跳过"); + continue; + } + + // 每个文件独立处理阶段号 + int defaultNum = fi + 1; + buildSingleFileStages( + sections, name, + merged, defaultNum); + + fileCount++; + } + + if (merged.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + name + + ": 无有效任务数据"); + questCache.put(name, + new ArrayList<>()); + return; + } + + // 日志 + for (Map.Entry en + : merged.entrySet()) { + int num = en.getKey() >= 1000 + ? en.getKey() - 999 + : en.getKey(); + plugin.getLogger().info( + "[QuestTracker] " + name + + " 阶段" + num + + " \"" + + en.getValue().displayName + + "\" → 条件" + + en.getValue() + .conditions.size() + + "项, 详情" + + en.getValue() + .details.size() + + "项, 奖励" + + en.getValue() + .rewards.size() + + "项"); + } + + List list = + new ArrayList<>( + merged.values()); + questCache.put(name, list); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 解析完成 → " + + list.size() + " 个阶段" + + " (来自 " + fileCount + + " 个文件)"); + } + + private Integer extractStageNumber( + String title) { + if (title == null) return null; + String t = title.toLowerCase().trim(); + + // 含阶段/stage/关/chapter 关键词 + boolean isStage = + t.contains("阶段") + || t.contains("stage") + || t.contains("关卡") + || t.contains("章节") + || t.contains("chapter"); + + if (isStage) { + String digits = title + .replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + try { + return Integer.parseInt(digits); + } catch (NumberFormatException e) { + // fall through + } + } + return parseChineseNumber(title); + } + + // 无关键词但以数字开头(如 "1. 挖掘") + String trimmed = t + .replaceAll("^[.、\\s]+", "") + .trim(); + if (trimmed.length() > 0 + && Character.isDigit( + trimmed.charAt(0))) { + String num = trimmed + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) { + try { + return Integer.parseInt(num); + } catch (NumberFormatException e) { + // fall through + } + } + } + + return null; + } +// ========== 领取记录 ========== + + /** + * 标记已领取某阶段奖励 + */ + public void markClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return; + // 用阶段编号代替显示名,保证稳定 + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + storage.setCondition(player, key); + } + + public boolean hasClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return false; + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + return toBool( + storage.getCondition( + player, key)); + } + + private int parseChineseNumber( + String title) { + String[] cn = {"一", "二", "三", + "四", "五", "六", "七", + "八", "九", "十"}; + if (title.contains("十")) { + for (int j = 0; j < 9; j++) { + if (title.contains(cn[j])) { + return 10 + j + 1; + } + } + return 10; + } + for (int i = 0; i < 9; i++) { + if (title.contains(cn[i])) { + return i + 1; + } + } + return 0; + } + + // ========== md 解析核心 ========== + + /** + * 读取文件所有行 + */ + private List readFileLines( + File file) { + List lines = + new ArrayList<>(); + try { + BufferedReader br = + new BufferedReader( + new FileReader(file)); + String line; + while ((line = br.readLine()) + != null) { + lines.add(line); + } + br.close(); + } catch (Exception e) { + plugin.getLogger().warning( + "[QuestTracker] 读取 " + + file.getName() + + " 失败: " + + e.getMessage()); + } + return lines; + } + + /** + * 自动检测格式并解析单个文件 + */ + private List parseMdFile( + File file, String category) { + List lines = + readFileLines(file); + if (lines.isEmpty()) + return new ArrayList<>(); + + // 检测分隔符 + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) + hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + // # 分隔格式 + return parseSections( + lines, category); + } else if (hasDash) { + // ---- 分隔格式:转换为 # 后复用 + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + title = "separator"; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseSections( + converted, category); + } else { + // 无分隔符:整文件一个阶段 + return parseFlat(lines, category); + } + } + + /** + * # 分隔格式 → 区块 → 阶段 + */ + private List parseSections( + List lines, + String category) { + // 拆分为区块 + List
sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + + // # 开头 → 新区块 + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add( + new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } + // 空行/分隔线 → 跳过 + else if (t.isEmpty() + || t.startsWith("----") + || t.startsWith("====") + || t.startsWith("***")) { + // skip + } + // 普通行 → 区块内容 + else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + curLines.add(cond); + } + } + } + // 保存最后一个区块 + if (curTitle != null) { + sections.add( + new Section( + curTitle, curLines)); + } + + // 直接转为 QuestFile 列表 + List result = + new ArrayList<>(); + int num = 1; + for (Section sec : sections) { + QuestFile qf = new QuestFile(); + qf.displayName = + extractDisplayName( + sec.title); + qf.fileName = category; + qf.conditions = + new ArrayList<>(sec.lines); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + result.add(qf); + num++; + } + return result; + } + + /** + * 无分隔符:整文件 = 一个阶段 + */ + private List parseFlat( + List lines, + String category) { + List list = + new ArrayList<>(); + QuestFile qf = makeDefaultStage( + category); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + qf.conditions.add(cond); + } + } + if (!qf.conditions.isEmpty()) { + list.add(qf); + } + return list; + } +// ========== Vault 经济奖励发放 ========== + + /** + * 发放阶段奖励 + * 含经济内容的走 Vault API或债券 + */ + public void claimRewards( + Player player, + QuestFile qf) { + if (qf == null + || qf.rewards == null + || qf.rewards.isEmpty()) { + player.sendMessage("§c无可领取的奖励"); + return; + } + + Main main = (Main) plugin; + // ★ 读取配置:economy = Vault,bonds = 债券(默认) + boolean useEconomy = "economy".equalsIgnoreCase( + main.getConfigMgr().rewardChannel); + + // Vault 经济引用(仅 useEconomy 时使用) + net.milkbowl.vault.economy.Economy econ = null; + if (useEconomy) { + econ = main.getEconomy(); + } + + int sentCount = 0; + + for (String reward : qf.rewards) { + String rt = reward.trim(); + if (rt.isEmpty()) continue; + + // 检查可选奖励标记 + if (hasOptionalMarker(rt)) { + if (!areAllConditionsMet(player.getName(), qf)) { + player.sendMessage( + "§7[可选奖励] 需完成所有可选条件: " + rt); + continue; + } + } + + // 按 + 分割子项 + String[] parts = rt.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 尝试识别经济金额 + double amt = extractEconomyAmount(part); + if (amt > 0) { + // ★ 根据配置分发 + if (useEconomy && econ != null) { + // —— Vault 经济 —— + double before = + econ.getBalance(player); + econ.depositPlayer(player, amt); + double after = + econ.getBalance(player); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(余额: §e" + + String.format("%.2f", before) + + " §7→ §a" + + String.format("%.2f", after) + + "§7)"); + if (plugin instanceof Main) { + ((Main) plugin).getCommission() + .payCommission( + player.getName(), amt); + } + } else { + // —— 债券(默认) —— + int bondAmt = + (int) Math.round(amt); + BondManager bonds = main.getBonds(); + if (bonds != null) { + int before = bonds.getBonds( + player.getName()); + bonds.addBonds( + player.getName(), + bondAmt, + "quest_reward", + qf.displayName, + "任务系统", + "任务奖励:" + qf.displayName); + int after = bonds.getBonds( + player.getName()); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(债券: §e" + + before + + " §7→ §a" + + after + "§7)"); + } else { + player.sendMessage( + "§c债券系统不可用"); + } + } + sentCount++; + continue; + } + + // 尝试发物品 + java.util.List items = + parseRewardItems(part); + if (!items.isEmpty()) { + for (ItemStack is : items) { + player.getInventory().addItem(is); + } + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + continue; + } + + // 非经济非物品 → 仅消息 + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + } + } + + plugin.getLogger().info( + "[QuestTracker] " + player.getName() + + " 预取了 \"" + qf.displayName + + "\" 共 " + sentCount + "项奖励"); + } + + /** + * 检查奖励文本是否含可选标记 + */ + private boolean hasOptionalMarker( + String text) { + return text.contains("(可选)") + || text.contains("(进阶)") + || text.contains("【可选】") + || text.contains("【进阶】"); + } + + /** + * 检查阶段所有条件(含可选)是否全部满足 + * 用于决定可选奖励是否发放 + */ + private boolean areAllConditionsMet( + String player, QuestFile qf) { + if (qf.conditions == null + || qf.conditions.isEmpty()) + return true; + for (String cond : qf.conditions) { + if (!isConditionMet(player, cond)) + return false; + } + return true; + } + + /** + * 从奖励文本中提取经济金额 + * 支持: "500游戏币" "500" "金币500" + * "$500" "1000-5000"(随机) "1000~5000" + */ + private double extractEconomyAmount( + String text) { + String t = text.replaceAll(" ", ""); + + // 必须包含经济关键词才算经济奖励 + boolean hasEcon = + t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额") + || t.contains("发钱") + || t.contains("债券"); // ★ 新增 + + + if (!hasEcon) return 0; + + // 范围: "1000-5000" 或 "1000~5000" + Matcher rm = Pattern + .compile("(\\d+)[-~](\\d+)") + .matcher(t); + if (rm.find()) { + double min = Double.parseDouble( + rm.group(1)); + double max = Double.parseDouble( + rm.group(2)); + return min + Math.random() + * (max - min); + } + + // 纯数字(含 $ 前缀) + Matcher nm = Pattern + .compile("\\$?(\\d+(\\.\\d+)?)") + .matcher(t); + if (nm.find()) { + return Double.parseDouble( + nm.group(1)); + } + + return 0; + } + /** + * 解析奖励物品文本 + * "皮革套装*1" → 4件皮革装备 + * "木剑*1" → 1把木剑 + * "钻石*64" → 64颗钻石 + */ + private List parseRewardItems( + String text) { + List result = + new ArrayList<>(); + + // 去掉条件前缀 + // 如 "完成注册登录:皮革套装*1" + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + + // 按 + 分割多个物品 + String[] parts = + text.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 跳过经济文本 + if (extractEconomyAmount(part) > 0) + continue; + + int amount = 1; + String itemName = part; + + // 解析 *N 数量 + if (part.contains("*")) { + String[] sp = + part.split("\\*"); + itemName = sp[0].trim(); + try { + amount = Integer.parseInt( + sp[1].trim()); + } catch (Exception e) { + amount = 1; + } + } + + List mats = + matchMaterials(itemName); + for (Material m : mats) { + int give = m.name() + .contains("HELMET") + || m.name() + .contains("CHESTPLATE") + || m.name() + .contains("LEGGINGS") + || m.name() + .contains("BOOTS") + ? 1 : amount; + result.add( + new ItemStack(m, give)); + } + } + return result; + } + + /** + * 中文物品名 → Material 列表 + * 套装返回多个部件 + */ + private List matchMaterials( + String name) { + List list = + new ArrayList<>(); + String n = name.toLowerCase().trim(); + + // 套装 + if (n.contains("皮革套装") + || n.contains("皮套装")) { + list.add(Material.LEATHER_HELMET); + list.add(Material.LEATHER_CHESTPLATE); + list.add(Material.LEATHER_LEGGINGS); + list.add(Material.LEATHER_BOOTS); + return list; + } + if (n.contains("铁套装") + || n.contains("铁甲")) { + list.add(Material.IRON_HELMET); + list.add(Material.IRON_CHESTPLATE); + list.add(Material.IRON_LEGGINGS); + list.add(Material.IRON_BOOTS); + return list; + } + if (n.contains("钻石套装") + || n.contains("钻甲")) { + list.add(Material.DIAMOND_HELMET); + list.add(Material.DIAMOND_CHESTPLATE); + list.add(Material.DIAMOND_LEGGINGS); + list.add(Material.DIAMOND_BOOTS); + return list; + } + // 木制工具套装 + if (n.contains("木制工具") + || n.contains("木工具")) { + list.add(Material.WOODEN_SWORD); + list.add(Material.WOODEN_PICKAXE); + list.add(Material.WOODEN_AXE); + list.add(Material.WOODEN_SHOVEL); + return list; + } + if (n.contains("石制工具") + || n.contains("石工具")) { + list.add(Material.STONE_SWORD); + list.add(Material.STONE_PICKAXE); + list.add(Material.STONE_AXE); + list.add(Material.STONE_SHOVEL); + return list; + } + if (n.contains("铁工具")) { + list.add(Material.IRON_SWORD); + list.add(Material.IRON_PICKAXE); + list.add(Material.IRON_AXE); + list.add(Material.IRON_SHOVEL); + return list; + } + + // 单个物品 + if (n.contains("木剑")) + list.add(Material.WOODEN_SWORD); + else if (n.contains("木镐")) + list.add(Material.WOODEN_PICKAXE); + else if (n.contains("铁剑")) + list.add(Material.IRON_SWORD); + else if (n.contains("铁镐")) + list.add(Material.IRON_PICKAXE); + else if (n.contains("钻石剑")) + list.add(Material.DIAMOND_SWORD); + else if (n.contains("钻石镐")) + list.add(Material.DIAMOND_PICKAXE); + else if (n.contains("钻石")) + list.add(Material.DIAMOND); + else if (n.contains("铁锭")) + list.add(Material.IRON_INGOT); + else if (n.contains("金锭") + || n.contains("金块")) + list.add(Material.GOLD_INGOT); + else if (n.contains("煤炭")) + list.add(Material.COAL); + else if (n.contains("木棍")) + list.add(Material.STICK); + else if (n.contains("弓")) + list.add(Material.BOW); + else if (n.contains("箭")) + list.add(Material.ARROW); + else if (n.contains("床")) + list.add(Material.RED_BED); + else { + // 尝试直接匹配 Material 名 + try { + list.add(Material.valueOf( + n.toUpperCase() + .replaceAll("[^A-Z_]", + ""))); + } catch (Exception e) { + // 未知物品 + } + } + return list; + } + + private QuestFile getOrCreateStage( + TreeMap map, + int key, + String category) { + if (!map.containsKey(key)) { + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + key; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + } + return map.get(key); + } + + /** + * 单个文件的区块 → 阶段 + * defaultStageNum: 文件无阶段标题时的默认编号 + */ + private void buildSingleFileStages( + List
sections, + String category, + TreeMap stageMap, + int defaultStageNum) { + Integer curKey = null; + + for (Section sec : sections) { + Integer stageNum = + extractStageNumber(sec.title); + + if (stageNum != null) { + curKey = stageNum; + } else if (curKey == null) { + curKey = defaultStageNum; + } + + QuestFile qf = getOrCreateStage( + stageMap, curKey, category); + + String displayName = + extractDisplayName(sec.title); + + // 设置显示名 + if (!displayName.isEmpty() + && qf.displayName + .startsWith("阶段 ")) { + qf.displayName = displayName; + } + + // 路由 + if (stageNum != null + && sec.lines.isEmpty()) { + if (!displayName.isEmpty()) { + qf.displayName = displayName; + } + } + else if (isRewardTitle(sec.title)) { + // 奖励 → 只进 rewards + qf.rewards.addAll(sec.lines); + } + else if (isDetailsTitle(sec.title)) { + // 详情 → 进 details + conditions + qf.details.addAll(sec.lines); + for (String line : sec.lines) { + if (!qf.conditions + .contains(line)) { + qf.conditions.add(line); + } + } + } + else if (isConditionsTitle( + sec.title)) { + // 条件 → 只进 conditions + qf.conditions.addAll(sec.lines); + } + else { + // 其他 → 条件 + qf.conditions.addAll(sec.lines); + } + } + } + + /** + * "主线任务-斗志昂扬" → "斗志昂扬" + * "支线-钓鱼" → "钓鱼" + * "阶段 1" → "阶段 1" + * "第一阶段" → "第一阶段" + */ + private String extractDisplayName( + String title) { + if (title == null) return ""; + // 按 - 或 — 或 – 分割 + String[] parts = title.split( + "\\s*[-—–]\\s*", 2); + if (parts.length >= 2) { + String right = parts[1].trim(); + if (!right.isEmpty()) { + return right; + } + } + return title; + } + private DatabaseManager getDb() { + return ((Main) plugin).getDb(); + } + + private boolean isDetailsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("任务详情") + || t.contains("详情") + || t.contains("任务说明") + || t.contains("任务目标") + || t.contains("description") + || t.contains("任务要求"); + } + + private boolean isConditionsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("解锁条件") + || t.contains("条件") + || t.contains("前置条件") + || t.contains("前置要求") + || t.contains("解锁") + || t.contains("condition"); + } + + + private void ensureStage( + TreeMap map, + String category, + QuestFile current, + int counter) { + if (current != null) return; + // 用大数字确保排在有编号阶段后面 + int key = 1000 + counter; + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + (counter + 1); + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + // 更新调用方的引用(通过返回值) + // 这里用 map 操作,current 无法直接更新 + // 所以改用返回值方式 + } + /** + * 自动将条件中的奖励内容移到 rewards + */ + private void separateRewards(QuestFile qf) { + List remaining = + new ArrayList<>(); + for (String line : qf.conditions) { + if (isRewardContent(line)) { + if (!qf.rewards.contains(line)) { + qf.rewards.add(line); + } + } else { + remaining.add(line); + } + } + qf.conditions = remaining; + } + private boolean checkStageDependency( + String player, + String condition) { + Matcher m = Pattern.compile( + "第([一二三四五六七八九十\\d]+)" + + "阶段") + .matcher(condition); + + while (m.find()) { + String numStr = m.group(1); + int stageNum = + parseChineseNumber(numStr); + if (stageNum == 0) { + try { + stageNum = + Integer.parseInt(numStr); + } catch (Exception e) { + return false; + } + } + + boolean stageDone = false; + for (List quests + : questCache.values()) { + for (QuestFile qf : quests) { + Integer qfNum = + extractStageNumber( + qf.displayName); + if (qfNum != null + && qfNum == stageNum) { + if (isStageComplete(player, + qf.conditions)) { + stageDone = true; + } + break; + } + } + if (stageDone) break; + } + + if (!stageDone) return false; + } + return true; + } + + /** + * 判断一行内容是否为奖励 + */ + private boolean isRewardContent( + String line) { + String l = line.toLowerCase(); + // 含货币关键词 + if (l.contains("游戏币") + || l.contains("金币") + || l.contains("积分") + || l.contains("钻石") + || l.contains("发钱") + || l.contains("发放") + || l.contains("经验") + || l.contains("等级") + || l.contains("经济") + || l.contains("余额") + || l.contains("块钱")) + return true; + // 纯数字+单位(如 "500游戏币") + if (l.matches( + ".*\\d+\\s*(游戏币|金币|元|积分|" + + "钻石|块钱|经验|级|经济|余额).*")) + return true; + // $前缀(如 "$500") + if (l.matches(".*\\$\\d+.*")) + return true; + // 范围格式(如 "1000-5000" 或 "1000~5000") + if (l.matches(".*\\d+[-~]\\d+.*") + && !l.contains("阶段") + && !l.contains("stage")) + return true; + return false; + } + + // ========== 标题分类 ========== + + private boolean isStageTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("阶段") + || t.contains("stage") + || t.contains("第一") + || t.contains("第二") + || t.contains("第三") + || t.contains("第四") + || t.contains("第五"); + } + + private boolean isRewardTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("奖励") + || t.contains("奖品") + || t.contains("报酬") + || t.contains("reward") + || t.contains("奖赏") + || t.contains("礼包内容") + || t.contains("礼包") + || t.contains("物品奖励"); + } + + + private QuestFile makeDefaultStage( + String category) { + QuestFile qf = new QuestFile(); + qf.displayName = "第一阶段"; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + return qf; + } + + private static class Section { + final String title; + final List lines; + Section(String title, + List lines) { + this.title = title; + this.lines = lines; + } + } + + // ========== 公开查询 ========== + + public List getQuests( + String category) { + List list = + questCache.get(category); + return list != null ? list + : new ArrayList<>(); + } + + public int getCompletedStageCount( + String player, + List quests) { + int count = 0; + if (quests == null) return 0; + for (QuestFile qf : quests) { + if (isStageComplete(player, + qf.conditions)) { + count++; + } + } + return count; + } + + public QuestFile getCurrentStage( + String player, + List quests) { + if (quests == null) return null; + for (QuestFile qf : quests) { + if (!isStageComplete( + player, qf.conditions)) { + return qf; + } + } + return null; + } + + public int getStageNumber( + List quests, + QuestFile qf) { + if (quests == null || qf == null) + return 0; + return quests.indexOf(qf) + 1; + } + + public int getStageNumber( + QuestFile qf) { + if (qf == null) return 0; + List quests = + getQuests(qf.fileName); + return getStageNumber(quests, qf); + } + + public boolean isStageCompleted( + String player, + QuestFile qf) { + if (qf == null) return false; + return isStageComplete( + player, qf.conditions); + } + + public List getUnmetConditions( + String player, + List conditions) { + List unmet = + new ArrayList<>(); + if (conditions == null) return unmet; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) { + unmet.add( + getProgress( + player, cond)); + } + } + return unmet; + } + public String getProgress( + String player, String rawCond) { + if (player == null || rawCond == null) + return "§7未知"; + String key = stripOptional(rawCond); + + if (key.contains("打卡") + && key.contains("群系")) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return "§7打卡 " + biome + + "群系: §e" + + catCount + "/1"; + } + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + + if (key.contains("完成") + && key.contains("阶段")) { + boolean done = checkStageDependency( + player, key); + return done + ? "§a✓ " + key + : "§c✗ " + key; + } + + if ("完成注册登录".equals(key)) { + boolean done = getDb() != null + && getDb().userExists( + player); + return done + ? "§a✓ 完成注册登录" + : "§c✗ 完成注册登录"; + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + boolean done = val != null + && !val.toString().isEmpty(); + return done + ? "§a✓ 绑定Email" + : "§c✗ 绑定Email"; + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + needed + "小时"; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + int cur = getCounter( + player, "CHAT"); + return "§7聊天: §e" + + cur + "/1"; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return "§7方块操作: §e" + + total + "/" + needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return "§7连续登录: §e" + + streak + "/" + needed + "天"; + } + + if (key.startsWith("MINE:")) { + String[] parts = key.split(":"); + if (parts.length >= 3) { + int needed = + parseInt(parts[2]); + int cur = getCounter( + player, + "MINE:" + parts[1]); + return "§7挖 " + parts[1] + + ": §e" + cur + + "/" + needed; + } + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "VILLAGER_TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + + } + + // ===== 击杀泛解析(支持中文列表) ===== +// 匹配 "击杀10只骷髅、僵尸、苦力怕" 等 + if (key.contains("击杀") && key.contains("只")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + + String mobPart = key; + java.util.regex.Matcher killM = + java.util.regex.Pattern + .compile("击杀\\d+只(.+)") + .matcher(key); + if (killM.find()) { + mobPart = killM.group(1).trim(); + } + + String[] mobNames = mobPart + .split("[、,,,]+"); + int totalKills = 0; + StringBuilder names = new StringBuilder(); + for (String name : mobNames) { + String trimmed = name.trim(); + if (trimmed.isEmpty()) continue; + String entityType = + chineseMobToEntity(trimmed); + if (!entityType.isEmpty()) { + totalKills += getCounter( + player, + "KILL:" + entityType); + if (names.length() > 0) + names.append("/"); + names.append(trimmed); + } + } + return "§7击杀" + names + ": §e" + + totalKills + "/" + needed; + } + +// 单一KILL:xxx:N 格式(原有逻辑保留) + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + int needed = parseInt(p[2]); + int cur = getCounter(player, + "KILL:" + p[1]); + return "§7击杀" + p[1] + ": §e" + + cur + "/" + needed; + } + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "KILL_PLAYER"); + return "§7击杀玩家: §e" + + cur + "/" + needed; + } + + if (key.startsWith("CRAFT")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "CRAFT"); + return "§7合成: §e" + + cur + "/" + needed; + } + + if (key.startsWith("FISH")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "FISH"); + return "§7钓鱼: §e" + + cur + "/" + needed; + } + + if (key.startsWith("PLAYTIME")) { + String[] parts = key.split(":"); + long needed = parts.length >= 2 + ? Long.parseLong(parts[1]) + : 0; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + formatMs(needed); + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7上报村庄: §e" + + cur + "/" + needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7探索村庄: §e" + + cur + "/1"; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return "§7近7天登录: §e" + + streak + "/" + + needed + "天"; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7新手任务: §e" + + done + "/" + + quests.size(); + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7主线任务: §e" + + done + "/" + + quests.size(); + } + // ===== 泛解析:击杀类进度 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < mobTypes.size(); i++) { + total += getCounter(player, + "KILL:" + mobTypes.get(i)); + if (i > 0) names.append("/"); + names.append(reverseMobName( + mobTypes.get(i))); + } + return "§7击杀" + names + ": §e" + + total + "/" + needed; + } + } + + // ===== 泛解析:村民交易进度 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + // ===== 泛解析:采集/挖矿进度 ===== + if (key.contains("采集") + || key.contains("挖")) { + int needed = + parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < matTypes.size(); i++) { + total += getCounter( + player, + "MINE:" + matTypes + .get(i)); + if (i > 0) + names.append("/"); + names.append( + translateMaterialName( + matTypes + .get(i))); + } + return "§7采集" + names + + ": §e" + total + + "/" + needed; + } + } + + // ===== 泛解析:合成进度 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "CRAFT"); + return "§7合成: §e" + cur + "/" + needed; + } + + // ===== 泛解析:钓鱼进度 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "FISH"); + return "§7钓鱼: §e" + cur + "/" + needed; + } + + return "§7" + key; + } + private String translateMaterialName( + String name) { + switch (name) { + case "COAL_ORE": + return "煤矿石"; + case "DEEPSLATE_COAL_ORE": + return "深层煤矿石"; + case "IRON_ORE": + return "铁矿石"; + case "DEEPSLATE_IRON_ORE": + return "深层铁矿石"; + case "COPPER_ORE": + return "铜矿石"; + case "DEEPSLATE_COPPER_ORE": + return "深层铜矿石"; + case "GOLD_ORE": + return "金矿石"; + case "DEEPSLATE_GOLD_ORE": + return "深层金矿石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "DEEPSLATE_DIAMOND_ORE": + return "深层钻石矿石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "DEEPSLATE_EMERALD_ORE": + return "深层绿宝石矿石"; + case "LAPIS_ORE": + return "青金石矿石"; + case "DEEPSLATE_LAPIS_ORE": + return "深层青金石矿石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "DEEPSLATE_REDSTONE_ORE": + return "深层红石矿石"; + case "NETHER_GOLD_ORE": + return "下界金矿石"; + case "NETHER_QUARTZ_ORE": + return "下界石英矿石"; + case "ANCIENT_DEBRIS": + return "远古残骸"; + case "COAL": + return "煤炭"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "RAW_COPPER": + return "粗铜"; + case "DIAMOND": + return "钻石"; + case "EMERALD": + return "绿宝石"; + case "LAPIS_LAZULI": + return "青金石"; + case "REDSTONE": + return "红石"; + case "QUARTZ": + return "石英"; + default: + return name; + } + } + + /** + * 中文物种名 → Bukkit EntityType 名 + * 支持击杀泛解析的模糊匹配 + */ + private String chineseMobToEntity(String cn) { + String n = cn.toLowerCase().trim(); + + // 直接别名表 + Map mobMap = new HashMap<>(); + mobMap.put("骷髅", "SKELETON"); + mobMap.put("僵尸", "ZOMBIE"); + mobMap.put("苦力怕", "CREEPER"); + mobMap.put("蜘蛛", "SPIDER"); + mobMap.put("洞穴蜘蛛", "CAVE_SPIDER"); + mobMap.put("小白", "SKELETON"); + mobMap.put("末影人", "ENDERMAN"); + mobMap.put("enderman", "ENDERMAN"); + mobMap.put("猪灵", "PIGLIN"); + mobMap.put("猪灵蛮兵", "PIGLIN_BRUTE"); + mobMap.put("恶魂", "GHAST"); + mobMap.put("凋灵骷髅", "WITHER_SKELETON"); + mobMap.put("烈焰人", "BLAZE"); + mobMap.put("史莱姆", "SLIME"); + mobMap.put("岩浆怪", "MAGMA_CUBE"); + mobMap.put("女巫", "WITCH"); + mobMap.put("蠹虫", "SILVERFISH"); + mobMap.put("潜影贝", "SHULKER"); + mobMap.put("守卫者", "GUARDIAN"); + mobMap.put("远古守卫者", "ELDER_GUARDIAN"); + mobMap.put("溺尸", "DROWNED"); + mobMap.put("僵尸村民", "ZOMBIE_VILLAGER"); + mobMap.put("尸壳", "HUSK"); + mobMap.put("流浪者", "STRAY"); + mobMap.put("幻翼", "PHANTOM"); + mobMap.put("掠夺者", "PILLAGER"); + mobMap.put("卫道士", "VINDICATOR"); + mobMap.put("唤魔者", "EVOKER"); + mobMap.put("恼鬼", "VEX"); + mobMap.put("劫掠兽", "RAVAGER"); + mobMap.put("蜘蛛骑士", "SPIDER_JOCKEY"); + mobMap.put("凋灵", "WITHER"); + mobMap.put("末影龙", "ENDER_DRAGON"); + + // 精确匹配 + if (mobMap.containsKey(n)) { + return mobMap.get(n); + } + + // 模糊匹配(包含关系) + for (Map.Entry en + : mobMap.entrySet()) { + if (n.contains(en.getKey()) + || en.getKey().contains(n)) { + return en.getValue(); + } + } + + // 尝试直接用英文名 + try { + return org.bukkit.entity.EntityType + .valueOf(n.toUpperCase() + .replace(" ", "_")) + .name(); + } catch (Exception e) { + return ""; + } + } + + public List checkInBiome( + String player, + String rawBiomeKey, + String worldName, + double x, double y, double z) { + List matched = + new ArrayList<>(); + + // 32格去重 + String posKey = + player + "_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + String lastPos = + lastCheckInPos.get(posKey); + if (curPos.equals(lastPos)) { + plugin.getLogger().info( + "[QuestTracker] 打卡忽略: " + + player + + " 32格内重复"); + return matched; + } + lastCheckInPos.put(posKey, curPos); + + String category = + mapBiomeCategory( + rawBiomeKey, worldName); + // 结构检测(覆盖纯生物群系分类) + // 地下检测:Y低于表面则归类为地下 +// 覆盖废弃矿井/试炼密室/要塞/远古城市等 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + + // ===== 结构检测(覆盖纯生物群系分类)===== + // 使用 PaperMC API 检测玩家所在位置的结构 + String structureCat = + detectStructureAt( + worldName, x, y, z); + if (structureCat != null + && !structureCat.isEmpty()) { + // 结构检测成功,结构分类优先 + // (站在结构上就不算"地下") + category = structureCat; + plugin.getLogger().info( + "[QuestTracker] 结构覆盖: " + + category + + " (结构: " + + structureCat + ")"); + } else { + plugin.getLogger().info( + "[QuestTracker] 未检测到结构: world=" + + worldName + ", pos=" + + String.format("%.0f,%.0f,%.0f", x, y, z)); + } + + + // 记录总打卡 + int allCur = getCounter( + player, "biome_ALL"); + setCounter(player, "biome_ALL", + allCur + 1); + + // 记录分类打卡 + if (!category.isEmpty() + && !"主世界" + .equals(category)) { + String catKey = + "biomecat_" + category; + int catCur = getCounter( + player, catKey); + setCounter(player, catKey, + catCur + 1); + } + + // 记录具体群系 + String bioKey = + "biome_" + rawBiomeKey; + int bioCur = getCounter( + player, bioKey); + setCounter(player, bioKey, + bioCur + 1); + + // 检查所有任务条件是否匹配 + List categories = + Arrays.asList( + "新人任务", + "主线任务", + "支线任务"); + + for (String cat : categories) { + List quests = + getQuests(cat); + for (QuestFile qf : quests) { + if (isStageCompleted( + player, qf)) + continue; + for (String cond + : qf.conditions) { + if (isOptional(cond)) + continue; + String condKey = + stripOptional(cond); + if (!condKey + .contains("打卡")) + continue; + // 支持群系和结构两种打卡条件 + boolean isBiomeCond = + condKey.contains("群系") + || condKey + .contains("风景点"); + boolean isStructureCond = + condKey.contains("结构") + || condKey + .contains("村庄") + || condKey + .contains("神殿") + || condKey + .contains("神庙") + || condKey + .contains("小屋") + || condKey + .contains("雪屋") + || condKey + .contains("府邸") + || condKey + .contains("前哨站") + || condKey + .contains("矿井") + || condKey + .contains("要塞") + || condKey + .contains("城市") + || condKey + .contains("废墟") + || condKey + .contains("密室") + || condKey + .contains("沉船") + || condKey + .contains("宝箱") + || condKey + .contains("传送门") + || condKey + .contains("化石") + || condKey + .contains("堡垒"); + if (!isBiomeCond + && !isStructureCond) + continue; + String target = + extractBiome(condKey); + plugin.getLogger().info( + "[QuestTracker] 条件匹配: condKey=\"" + + condKey + "\", target=\"" + + target + "\", category=\"" + + category + "\", isBiomeCond=" + + isBiomeCond + ", isStructureCond=" + + isStructureCond); + boolean hit = false; + if ("ALL".equals(target) + || "风景点" + .equals(target)) { + hit = true; + } else if (!target.isEmpty() + && target.equals( + category)) { + hit = true; + } + if (hit && !matched + .contains(cond)) { + matched.add(cond); + } + } + } + } + + return matched; + } + + /* + * 打卡入口:报告坐标 → 32格去重 → 匹配条件 → 记录 + */ + public String checkInAtPosition( + String player, String worldName, + double x, double y, double z) { + + List history = + loadCheckInPositions(player); + for (double[] pos : history) { + double dist = Math.sqrt( + Math.pow(x - pos[0], 2) + + Math.pow(y - pos[1], 2) + + Math.pow(z - pos[2], 2)); + if (dist <= 32) { + return "§c距离上次打卡点太近(" + + String.format( + "%.0f", dist) + + "格)"; + } + } + + // ===== 获取群系和分类 ===== + String biome = "minecraft:plains"; + String category = "平原"; + String structureCat = null; + try { + org.bukkit.World w = + Bukkit.getWorld(worldName); + if (w != null) { + org.bukkit.Location loc = + new org.bukkit.Location( + w, + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + biome = loc.getBlock() + .getBiome().getKey() + .toString(); + category = mapBiomeCategory( + biome, worldName); + + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + + // ===== 结构检测(覆盖纯生物群系分类)===== + structureCat = + detectStructureAt( + worldName, x, y, z); + plugin.getLogger().info( + "[QuestTracker] checkInAtPosition 结构检测: " + + structureCat + " at " + + String.format("%.0f,%.0f,%.0f", x, y, z)); + if (structureCat != null + && !structureCat.isEmpty()) { + // 结构分类优先于"地下" + category = structureCat; + } + + if ("下界".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.NETHER) { + category = "下界"; + } else if ("末地".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.THE_END) { + category = "末地"; + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 群系读取失败: " + + ex.getMessage()); + } + + plugin.getLogger().info( + "[QuestTracker] 打卡: " + + player + + " world=" + worldName + + " biome=" + biome + + " cat=" + category + + " pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + + saveCheckInPosition( + player, x, y, z); + recordCheckIn( + player, biome, category, + worldName, x, y, z); + List matched = + checkInBiome( + player, biome, + worldName, x, y, z); + + StringBuilder sb = new StringBuilder(); + sb.append("§a打卡成功!群系: §e") + .append(category); + if (structureCat != null && !structureCat.isEmpty()) { + sb.append("\n§a结构: §e").append(structureCat); + } + if (!matched.isEmpty()) { + sb.append("\n§a完成任务条件:"); + for (String m : matched) { + String qn = + findQuestName(m); + sb.append("\n§7• "); + if (qn != null) { + sb.append("§e[") + .append(qn) + .append("§7] §f"); + } + sb.append(m); + } + } + return sb.toString(); + } + + + /** + * 根据条件文本查找所属任务名 + */ + private String findQuestName( + String condition) { + for (String cat : Arrays.asList( + "新人任务", + "主线任务", + "支线任务")) { + for (QuestFile qf : + getQuests(cat)) { + if (qf.conditions.contains( + condition)) { + return qf.displayName; + } + } + } + return null; + } + + + /** + * 加载玩家所有历史打卡坐标 + * 格式: x1,y1,z1;x2,y2,z2;... + */ + private List loadCheckInPositions( + String player) { + List list = new ArrayList<>(); + Object val = storage.getCondition( + player, "checkin_all_pos"); + if (val == null) return list; + String data = val.toString(); + if (data.isEmpty()) return list; + String[] records = data.split(";"); + for (String r : records) { + String[] parts = r.split(","); + if (parts.length >= 3) { + try { + list.add(new double[]{ + Double.parseDouble(parts[0]), + Double.parseDouble(parts[1]), + Double.parseDouble(parts[2])}); + } catch (NumberFormatException e) { + // 跳过损坏数据 + } + } + } + return list; + } + + /** + * 保存打卡坐标(追加到已有记录) + */ + private void saveCheckInPosition( + String player, double x, + double y, double z) { + List list = + loadCheckInPositions(player); + list.add(new double[]{x, y, z}); + StringBuilder sb = new StringBuilder(); + for (double[] pos : list) { + if (sb.length() > 0) sb.append(";"); + sb.append(pos[0]).append(",") + .append(pos[1]).append(",") + .append(pos[2]); + } + storage.setConditionRaw( + player, + "checkin_all_pos", + sb.toString()); + } + // ========== 中文生物名 → EntityType 映射 ========== + private static final Map + MOB_NAME_MAP = new HashMap<>(); + static { + MOB_NAME_MAP.put("洞穴蜘蛛", "CAVE_SPIDER"); + MOB_NAME_MAP.put("骷髅", "SKELETON"); + MOB_NAME_MAP.put("僵尸", "ZOMBIE"); + MOB_NAME_MAP.put("苦力怕", "CREEPER"); + MOB_NAME_MAP.put("蜘蛛", "SPIDER"); + MOB_NAME_MAP.put("末影人", "ENDERMAN"); + MOB_NAME_MAP.put("女巫", "WITCH"); + MOB_NAME_MAP.put("烈焰人", "BLAZE"); + MOB_NAME_MAP.put("僵尸猪灵", + "ZOMBIFIED_PIGLIN"); + MOB_NAME_MAP.put("猪灵", "PIGLIN"); + MOB_NAME_MAP.put("恶魂", "GHAST"); + MOB_NAME_MAP.put("凋灵骷髅", + "WITHER_SKELETON"); + MOB_NAME_MAP.put("史莱姆", "SLIME"); + MOB_NAME_MAP.put("岩浆怪", "MAGMA_CUBE"); + MOB_NAME_MAP.put("溺尸", "DROWNED"); + MOB_NAME_MAP.put("尸壳", "HUSK"); + MOB_NAME_MAP.put("流髑", "STRAY"); + MOB_NAME_MAP.put("幻翼", "PHANTOM"); + MOB_NAME_MAP.put("守卫者", "GUARDIAN"); + MOB_NAME_MAP.put("远古守卫者", + "ELDER_GUARDIAN"); + MOB_NAME_MAP.put("掠夺者", "PILLAGER"); + MOB_NAME_MAP.put("卫道士", "VINDICATOR"); + MOB_NAME_MAP.put("唤魔者", "EVOKER"); + MOB_NAME_MAP.put("恼鬼", "VEX"); + MOB_NAME_MAP.put("劫掠兽", "RAVAGER"); + MOB_NAME_MAP.put("潜影贝", "SHULKER"); + MOB_NAME_MAP.put("末影螨", "ENDERMITE"); + MOB_NAME_MAP.put("凋灵", "WITHER"); + MOB_NAME_MAP.put("末影龙", "ENDER_DRAGON"); + MOB_NAME_MAP.put("监守者", "WARDEN"); + MOB_NAME_MAP.put("嗅探兽", "SNIFFER"); + MOB_NAME_MAP.put("僵尸村民", + "ZOMBIE_VILLAGER"); + } + + /** + * 从条件文本中提取所有匹配的生物类型 + * 按名称长度降序匹配,避免短名误匹配 + */ + private List extractMobTypes( + String text) { + List types = new ArrayList<>(); + List> sorted = + new ArrayList<>( + MOB_NAME_MAP.entrySet()); + sorted.sort((a, b) -> + b.getKey().length() + - a.getKey().length()); + String remaining = text; + for (Map.Entry entry + : sorted) { + if (remaining.contains( + entry.getKey())) { + if (!types.contains( + entry.getValue())) { + types.add(entry.getValue()); + } + remaining = remaining.replace( + entry.getKey(), ""); + } + } + return types; + } + + /** + * EntityType → 中文名 + */ + private String reverseMobName( + String entityType) { + for (Map.Entry entry + : MOB_NAME_MAP.entrySet()) { + if (entry.getValue() + .equals(entityType)) { + return entry.getKey(); + } + } + return entityType; + } + + /** + * 从条件文本中提取矿物名 + * "采集任意矿物(煤炭除外)1组" + * → [COAL_ORE, IRON_ORE, GOLD_ORE, ...] + */ + private List extractMaterialTypes( + String text) { + List types = new ArrayList<>(); + // 排除列表 + List excludes = + new ArrayList<>(); + if (text.contains("煤炭除外")) { + excludes.add("COAL"); + excludes.add("COAL_ORE"); + excludes.add("DEEPSLATE_COAL_ORE"); + } + // 通配符:"矿物" "矿石" + if (text.contains("矿物") + || text.contains("矿石")) { + String[] ores = { + "COAL_ORE", + "DEEPSLATE_COAL_ORE", + "IRON_ORE", + "DEEPSLATE_IRON_ORE", + "COPPER_ORE", + "DEEPSLATE_COPPER_ORE", + "GOLD_ORE", + "DEEPSLATE_GOLD_ORE", + "DIAMOND_ORE", + "DEEPSLATE_DIAMOND_ORE", + "EMERALD_ORE", + "DEEPSLATE_EMERALD_ORE", + "LAPIS_ORE", + "DEEPSLATE_LAPIS_ORE", + "REDSTONE_ORE", + "DEEPSLATE_REDSTONE_ORE", + "NETHER_GOLD_ORE", + "NETHER_QUARTZ_ORE", + "ANCIENT_DEBRIS" + }; + for (String ore : ores) { + if (!excludes.contains(ore)) { + types.add(ore); + } + } + return types; + } + // 单个矿物名 + if (text.contains("煤炭")) + types.add("COAL"); + if (text.contains("铁矿") + || text.contains("铁锭")) + types.add("RAW_IRON"); + if (text.contains("金矿") + || text.contains("金锭")) + types.add("RAW_GOLD"); + if (text.contains("钻石")) + types.add("DIAMOND"); + if (text.contains("绿宝石")) + types.add("EMERALD"); + if (text.contains("青金石")) + types.add("LAPIS_LAZULI"); + if (text.contains("红石")) + types.add("REDSTONE"); + if (text.contains("铜")) + types.add("RAW_COPPER"); + if (text.contains("下界石英") + || text.contains("石英")) + types.add("QUARTZ"); + return types; + } + + + /** + * 从条件文本中提取目标群系分类 + * "打卡上报地狱任意群系" → "下界" + * "打卡任意海洋群系" → "海洋" + * "打卡任意群系" → "ALL" + * "打卡任意风景点" → "" + * "打卡村庄" → "村庄" + * "打卡沙漠神殿" → "沙漠" + */ + private String extractBiome(String text) { + if (text == null) return ""; + + // 优先匹配结构名称(更具体) + String structureResult = + mapStructureCategory(text); + if (!structureResult.isEmpty()) { + return structureResult; + } + + // 逐个匹配分类关键词 + for (Map.Entry en + : CATEGORY_KEYWORDS.entrySet()) { + if (text.contains(en.getKey())) { + return en.getValue(); + } + } + if (text.contains("风景点") + || text.contains("风景")) { + return "ALL"; + } + + // 有"群系"但无具体分类 → ALL + if (text.contains("群系")) { + return "ALL"; + } + return ""; + } + + + // ========== 标记条件 ========== + + public void markConditionComplete( + String player, + String conditionName) { + if (player == null + || conditionName == null) + return; + storage.setCondition( + player, conditionName); + } + + // ========== 事件回调 ========== + + public void onBlockMined( + String player, String material) { + String key = "MINE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + // 总计数 + setCounter(player, "MINE:ALL", + getCounter(player, "MINE:ALL") + + 1); + } + public boolean onReportVillage( + String player, + String worldName, + double x, double y, double z) { + String dbKey = "village_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + Object lastObj = + storage.getCondition( + player, dbKey); + String lastPos = lastObj != null + ? lastObj.toString() : ""; + if (curPos.equals(lastPos)) { + return false; + } + storage.setConditionRaw( + player, dbKey, curPos); + + String key = "REPORT_VILLAGE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + return true; + } + + public void onMobKill( + String player, String mobType) { + String key = "KILL:" + mobType; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayerKill( + String player) { + String key = "KILL_PLAYER"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onItemCrafted( + String player) { + String key = "CRAFT"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onFishCaught( + String player) { + String key = "FISH"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayTime( + String player, long ms) { + String key = "PLAYTIME"; + long cur = getCounterLong( + player, key); + setCounter(player, key, cur + ms); + } + + // ========== 内部:条件判定 ========== + + private boolean isStageComplete( + String player, + List conditions) { + if (conditions == null + || conditions.isEmpty()) + return true; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) + return false; + } + return true; + } + + public boolean isConditionMet( + String player, + String rawCond) { + if (player == null + || rawCond == null) + return false; + + String key = stripOptional(rawCond); + + // 无条件类文本直接通过 + if (key.equals("无前级条件") + || key.equals("无条件") + || key.equals("无") + || key.equals("none") + || key.equals("无需条件") + || key.equals("无解锁条件")) { + return true; + } + + if (key.contains("打卡") + && (key.contains("群系") + || key.contains("风景点") + || key.contains("结构") + || key.contains("村庄") + || key.contains("神殿") + || key.contains("神庙") + || key.contains("小屋") + || key.contains("雪屋") + || key.contains("府邸") + || key.contains("前哨站") + || key.contains("矿井") + || key.contains("要塞") + || key.contains("城市") + || key.contains("废墟") + || key.contains("密室") + || key.contains("废墟") + || key.contains("沉船") + || key.contains("宝箱") + || key.contains("传送门") + || key.contains("化石") + || key.contains("堡垒"))) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return total >= 1; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return catCount >= 1; + } + return getCounter( + player, "biome_ALL") >= 1; + } + + if (key.contains("完成") + && key.contains("阶段")) { + return checkStageDependency( + player, key); + } + + if ("完成注册登录".equals(key)) { + return getDb() != null + && getDb().userExists( + player); + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + return val != null + && !val.toString().isEmpty(); + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return cur >= needed * 3600000L; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + return getCounter( + player, "CHAT") >= 1; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return total >= needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return streak >= needed; + } + + if (key.startsWith("MINE:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "MINE:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "KILL:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "VILLAGER_TRADE") + >= needed; + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter(player, + "KILL_PLAYER") >= n; + } + + if (key.startsWith("CRAFT")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "CRAFT") >= n; + } + + if (key.startsWith("FISH")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "FISH") >= n; + } + + if (key.startsWith("PLAYTIME")) { + String[] p = key.split(":"); + long n = p.length >= 2 + ? Long.parseLong(p[1]) : 0; + return getCounterLong(player, + "PLAYTIME") >= n; + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "REPORT_VILLAGE") + >= needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + return getCounter( + player, "REPORT_VILLAGE") + >= 1; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return streak >= needed; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + // ===== 泛解析:击杀类中文条件 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + for (String mobType : mobTypes) { + total += getCounter(player, + "KILL:" + mobType); + } + return total >= needed; + } + } + + // ===== 泛解析:村民交易条件 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "TRADE") + >= needed; + } + + // ===== 泛解析:采集/挖矿类条件 ===== + if (key.contains("采集") || key.contains("挖")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + for (String mat : matTypes) { + total += getCounter(player, + "MINE:" + mat); + } + return total >= needed; + } + } + + // ===== 泛解析:合成类条件 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "CRAFT") + >= needed; + } + + // ===== 泛解析:钓鱼类条件 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "FISH") + >= needed; + } + + return false; + } + + public void debugLogin(String player) { + try { + var user = getDb().getUser(player); + if (user != null) { + plugin.getLogger().info( + "[QuestTracker] " + + "=== " + player + + " 数据库全部字段 ==="); + for (var entry + : user.entrySet()) { + plugin.getLogger().info( + "[QuestTracker] " + + entry.getKey() + + " = " + + entry.getValue() + + " (" + + (entry.getValue() + != null + ? entry.getValue() + .getClass() + .getSimpleName() + : "null") + + ")"); + } + } else { + plugin.getLogger().info( + "[QuestTracker] " + + "getUser返回null: " + + player); + } + } catch (Exception e) { + plugin.getLogger().info( + "[QuestTracker] " + + "debug异常: " + + e.getMessage()); + } + } + + // ========== 内部:计数器 ========== + + private int getCounter( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + return 0; + } + private int getIntField( + String player, String field) { + try { + Object val = getDb().getField( + player, field); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val != null) { + return Integer.parseInt( + val.toString().trim()); + } + } catch (Exception e) { + // 忽略 + } + return 0; + } + + public void onVillagerTrade(String player) { + String key = "VILLAGER_TRADE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + private long getCounterLong( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).longValue(); + } + return 0; + } + + private void setCounter( + String player, String key, + int value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + private void setCounter( + String player, String key, + long value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + // ========== 工具 ========== + + private boolean toBool(Object val) { + if (val == null) return false; + if (val instanceof Boolean) + return (Boolean) val; + if (val instanceof Number) + return ((Number) val) + .intValue() != 0; + String s = String.valueOf(val); + return "1".equals(s) + || "true" + .equalsIgnoreCase(s); + } + + public boolean isOptional(String cond) { + if (cond == null) return false; + String c = cond; + return c.contains("(进阶)") + || c.contains("(可选)") + || c.contains(",可选)") + || c.contains(",可选)") + || c.contains("【可选】") + || c.contains("【进阶】"); + } + + + private String stripOptional(String cond) { + if (cond == null) return ""; + return cond.replace("(进阶)", "") + .replace("(可选)", "") + .replace(",可选)", "") + .replace(",可选)", "") + .replace("【可选】", "") + .replace("【进阶】", "") + .trim(); + } + + private int parseInt(String s) { + + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return 0; + } + } + private Object getDbField( + String player, String field) { + Main main = (Main) plugin; + return main.getDb().getField( + player, field); + } + + /** + * 从文本中提取第一个数字 + * "在线时间≥1小时" → 1 + * "破坏/放置方块100个" → 100 + */ + private int parseNumberFromText( + String text) { + java.util.regex.Matcher m = + java.util.regex.Pattern + .compile("(\\d+)") + .matcher(text); + if (m.find()) { + try { + return Integer.parseInt( + m.group(1)); + } catch (Exception e) { + return 0; + } + } + return 0; + } + public void onBlockPlaced( + String player, String material) { + String key = "PLACE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + setCounter(player, "PLACE:ALL", + getCounter(player, "PLACE:ALL") + + 1); + } + + public void onPlayerChat( + String player) { + String key = "CHAT"; + setCounter(player, key, + getCounter(player, key) + 1); + } + + private String formatMs(long ms) { + long sec = ms / 1000; + long min = sec / 60; + long hr = min / 60; + if (hr > 0) + return hr + "小时" + + (min % 60) + "分"; + if (min > 0) + return min + "分" + + (sec % 60) + "秒"; + return sec + "秒"; + } + + // ========== 数据结构 ========== + + public static class QuestFile { + public String displayName = ""; + public String fileName = ""; + public List details = + new ArrayList<>(); + public List rewards = + new ArrayList<>(); + public List conditions = + new ArrayList<>(); + + public boolean hasConditions() { + return conditions != null + && !conditions.isEmpty(); + } + } +} diff --git a/backup/README.md b/backup/README.md new file mode 100644 index 0000000..e74ef6a --- /dev/null +++ b/backup/README.md @@ -0,0 +1,569 @@ +# Sdf1系列登录插件使用说明 + +# 特别说明:本插件仅有简中语言版本,如需除简中以外的任何版本,请下载插件源码,自行修改! + +## 1. 功能介绍 + +| 名字 | 描述/作用 | 施工状态 | +| :--- | :--- | :--- | +| 登录 | 用户登录/注册 | 已完成 | +| 世界垃圾箱 | 清理服务器凋落物 | 已完成 | +| 工单系统 | 用户提交工单 | 已完成 | +| 签到系统 | 签到积分换奖励 | 已完成 | +| 新人礼包 | 新玩家7日礼包 | 已完成 | +| 挂机检测 | 挂机检测踢出 | 已完成 | +| 自定义菜单 | 可以自己diy指令 | 已完成 | +| 自定义菜单图标 | 可以用自己喜欢的物品当图标 | 已完工 | +| 商城系统| 内置游戏商人可供销售商品 | 已完工 | +| 虚拟货币 | 供签到、游戏商人使用的货币系统 | 已完工 | + + +## 2.安装说明: + +| 前置插件 | 选装类型 | 功能作用 | +| :--- | :--- | :--- | +| [CY_beibao](https://gitee.com/nihaoshidifu/CY_beibao) | 可选,推荐 | 云背包,积分商城可购买背包容量 +| EssentialsX | 可选 | 主流经济系统 +| [sdf1](https://gitee.com/nihaoshidifu/sdf1_plugn) | 可选,推荐 | CDK兑换系统 + + +### 2.1 下载 +您可以从GitHub和Gitee下载到插件,将其丢入服务器plugin目录,重启服务器即可。 + +### 2.2. 配置文件 + +#### 2.2.1 消息.txt +此文件控制着整个插件的所有提示消息,您可以根据自己的需求修改,修改后需要执行 /sdf1_login reload 重载插件。支持[颜色代码](https://zh.minecraft.wiki/w/%E6%A0%BC%E5%BC%8F%E5%8C%96%E4%BB%A3%E7%A0%81)
+同时兼容` `标签,对段落加粗,`\n 和
`进行段落换行 + + +```txt +# ========================================== +# Sdf1_login 消息配置文件 +# ========================================== +# 颜色代码使用 § 符号 +# §0黑色 §1深蓝 §2深绿 §3深青 +# §4深红 §5深紫 §6金色 §7灰色 +# §8深灰 §9蓝色 §a绿色 §b浅蓝 +# §c红色 §d粉色 §e黄色 §f白色 +# §l粗体 §n下划线 §o斜体 §r重置 +# +# 占位符用 {xxx} 表示,运行时自动替换 +# 支持 粗体 标签 +# 支持 \n 或
换行 +# 修改后执行 /sdf1_login reload 重载 +# ========================================== + +# ---- 登录注册相关 ---- +# login_timeout: 登录超时被踢出时的提示 +login_timeout=§c登录超时,请重新登录 +# not_registered: 未注册玩家收到的提示 +not_registered=§e您尚未注册,请使用 /reg <密码> +# not_logged_in: 已注册但未登录的提示 +not_logged_in=§e您尚未登录,请使用 /login <密码> +# already_registered: 已注册玩家再次注册时的提示 +already_registered=§c您已注册过账号 +# already_logged_in: 已登录玩家再次登录时的提示 +already_logged_in=§c您已登录 +# reg_success: 注册成功提示 +reg_success=§a注册成功!欢迎来到服务器 +# login_success: 登录成功提示 +login_success=§a登录成功! +# login_failed: 密码错误提示 +login_failed=§c密码错误,请重试 + +# ---- 密码相关 ---- +# password_wrong: 旧密码验证失败 +password_wrong=§c密码错误 +# password_changed: 密码修改成功 +password_changed=§a密码修改成功! +# password_format_error: 密码格式不符合要求 +password_format_error=§c密码需6位以上,含大小写字母和数字 +# password_same: 新密码与旧密码相同 +password_same=§c新密码不能与旧密码相同 +# reset_no_email: 找回密码时未绑定邮箱 +reset_no_email=§c您未绑定邮箱,无法找回密码 +# reset_sent: 临时密码已发送到邮箱 +reset_sent=§a临时密码已发送到您的邮箱,请查收 + +# ---- 签到积分相关 ---- +# checkin_already: 今日已签到 +checkin_already=§c您今日已签到 +# checkin_success: 签到成功,{points}=获得积分 {streak}=连续天数 {multi}=倍率 +checkin_success=§a签到成功!获得 {points} 积分,连续 {streak} 天,倍率 x{multi} +# points_insufficient: 积分不足 +points_insufficient=§c积分不足,当前: {points} +# points_purchase_success: 积分购买成功 +points_purchase_success=§a购买成功!消耗 {count} 积分 + +# ---- 挂机检测相关 ---- +# afk_set_enabled: 挂机检测开启 +afk_set_enabled=§a挂机检测已开启 +# afk_set_disabled: 挂机检测关闭 +afk_set_disabled=§c挂机检测已关闭 +# afk_kick: 因挂机被踢出 +afk_kick=§c因挂机过久被踢出 + +# ---- 邮箱相关 ---- +# email_set: 邮箱设置成功 +email_set=§a邮箱设置成功:{email} +# email_format_error: 邮箱格式错误 +email_format_error=§c邮箱格式不正确 + +# ---- 邀请相关 ---- +# invite_code_generated: 邀请码生成 +invite_code_generated=§a邀请码:{code} +# invite_used: 使用邀请码成功 +invite_used=§a邀请成功,邀请人:{player} +# invite_self: 使用自己的邀请码 +invite_self=§c不能使用自己的邀请码 +# invite_not_found: 邀请码不存在 +invite_not_found=§c邀请码不存在 +# invite_referral_bonus: 被邀请人签到后邀请人获得奖励 +invite_referral_bonus=§a邀请奖励!获得 {points} 积分 + +# ---- 新人礼包相关 ---- +# gift_not_ready: 礼包条件未满足 +gift_not_ready=§c礼包条件未满足 +# gift_claimed: 礼包领取成功 +gift_claimed=§a礼包领取成功! + +# ---- 审批与IP限制 ---- +# need_approval: 注册需审批 +need_approval=§e注册需审批 (工单#{id}) +# ip_max_accounts: IP注册已达上限 +ip_max_accounts=§cIP注册已达上限 + +# ---- 管理员操作 ---- +# admin_delete_confirm: 管理员删除账号确认提示 +admin_delete_confirm=§c请在聊天中输入玩家名确认删除 + +# ---- 聊天过滤 ---- +# chat_muted: 禁言玩家尝试聊天 +chat_muted=§c§l[Sdf1_chat] §f你已被禁言,无法聊天 +# chat_url_blocked: 发送违规链接被拦截 +# {url}=检测到的链接地址 +chat_url_blocked=§c§l[Sdf1_chat] §f检测到违规第三方链接: §e{url} +# chat_url_violation: 当前违规次数 +# {count}=当前违规次数 +chat_url_violation=§7当前违规次数: §e{count} +# chat_url_admin_notify: 通知管理员,{player}=玩家名 {url}=链接 {count}=次数 +chat_url_admin_notify=§e§l[Sdf1_chat] §f{player} 发送违规链接: {url} (第{count}次) +# chat_url_broadcast: 全服通报,{player}=玩家名 +chat_url_broadcast=§c§l[Sdf1_chat] §e{player} §f因发送违规链接被处罚 + +# ---- 通用 ---- +# 未知参数: 未知命令或权限不足时的提示 +未知参数=§c未知参数或权限不足 +# only_player: 仅玩家可用的命令 +# only_player=§c此命令仅玩家可用 + +``` + +### 2.2.2 挂机白名单.txt +当挂机踢出开启时,该配置下的玩家不受挂机提出影响 + +### 2.2.3 SMTP设置.txt + +此文件控制着SMTP邮件服务器的配置,用于找回密码时发送邮件。
可通过GUI进行配置,也可以手动修改此配置文件。修改后,重载插件即可生效 + +```txt +# ===== SMTP 邮件服务器配置 ===== +smtp地址=smtp.example.com +smtp端口=465 +smtp账号=mail@example.com +smtp密码=example.com +发件人名称=Sdf1_login +smtp加密=true +验证码接收邮箱=example@example.com + +``` + +### 2.2.4 插件设置.txt + +此配置管控这整个插件,包括玩家能注册多少个账号、默认的管理员面板密码多少、tag包含什么视为管理员等 + +```txt +# ===== Sdf1_login 插件设置 ===== +管理标签=admin +管理密码=qweasd +每IP最大账号数=3 +审批模式=manual +自动审批延迟分钟=30 +登录超时秒数=60 +挂机踢出=false +挂机超时秒数=300 +服务商积分倍率=2.0 +基础经济奖励=100 +经济奖励每评分=50.0 +报单人积分奖励=10 +# ---- 签到盲盒奖励 ---- +签到固定金额=0 +签到最小金额=10 +签到最大金额=100 +签到给积分=true +签到积分数量=10 + + +``` + +### 2.2.5 chat.txt +该文件控制着聊天过滤,当玩家发送违规链接时,会自动根据配置规则进行处理。
+可用的处罚规则: +- warn: 警告 +- mute: 禁言 +- kick: 踢出 +- ban: 封禁 +- banip:封禁IP + +```txt +# Sdf1_chat 聊天过滤配置 +通知管理员: true +全服通报: true +禁言时长: 300 + +白名单: +*.minecraft.net +baidu.com +github.com + +处罚规则: +1:warn:0 +2:warn:0 +3:mute:300 +5:mute:600 +8:kick:0 +10:ban:0 + +白名单玩家: + +``` +### 2.2.6 radio_config.txt +```txt +# ============ Sdf1 Radio Config ============ +# +# resource-pack-url +# 填外部地址(HTTP或HTTPS均可) +# 例: https://example.com/radiopack.zip +# 留空 = 使用内置HTTP服务 +# +# http-port +# 内置HTTP端口(仅resource-pack-url为空时生效) +# +# ============ Settings =============== + +resource-pack-url=https://*.ypshidifu.cn/|*.mcserver.ccwu.cc +http-port=443 +``` +- radio_config这里可以使用通配符域名,当使用通配符时,系统随机生成url,拒绝过资源包的玩家,每次都会收到提示。并且自1.2版本起,拒绝资源包=放弃游玩服务器。如果您无需广播功能,可以不放ogg文件进入`./plugin/radio/ogg`。这样服务端不会自动生成资源包,也不会强制下载了 + +### 2.2.7 菜单.txt +- 自定义菜单配置文件 + +```json +# 服务器菜单 +# 使用类json格式 +// 支持使用://Java单行注释、#yml单行注释、/* */块注释,也支持的块注释 + +{ + 标题:主城传送 + 指令:/dom tp 主城区 # 这里如果是xxx.txt则表示进入二级目录 + 权限类型:玩家 + 图标:COMPASS +} + +{ + 标题:随机传送 + 指令:/rtp + 权限类型:玩家 + 图标:ENDER_PEARL +} + +{ + 标题:擂台比武 + 指令:/dom tp PVP + 权限类型:玩家 + 图标:netherite_spear +} + +{ + 标题:领地操作 + 指令:/dom + 权限类型:玩家 + 图标:paper +} +{ + 标题:逛商店 + 指令:/dom tp shop + 权限类型:玩家 + 图标:honeycomb +} + +{ + 标题:传送开关 + 指令:/tptoggle + 权限类型:玩家 + 图标:redstone_torch +} + +{ + 标题:返回上一个位置 + 指令:/back + 权限类型:玩家 + 图标:red_bed +} +{ + 标题:前往小黑塔 + 指令:/dom tp 小黑塔 + 权限类型:玩家 + 图标:dragon_head +} +{ + 标题:云背包 + 指令:/cy + 权限类型:玩家 + 图标:potato +} +{ + 标题:自杀 + 指令:/suicide + 权限类型:玩家 + 图标:wooden_axe +} +{ + 标题:游戏商人 + 指令:/shop + 权限类型:玩家 + 图标:shield + 格子:28 +} +``` +### 2.2.8 商品配置 +- 配置此处的文件,负责展示游戏内所销售的商品。
+- 一个文件名,对应一个商品分类,使用md文件格式 +``` +# 建筑材料 + +| ID | 品名 | 材质 | 购入价 | 售出价 | 库存 | 本小时销量 | 总销量 | +| --- | --- | --- | --- | --- | --- | --- | --- | +| STONE | 石头 | STONE | 10 | -1 | -1 | 0 | 0 | +| COBBLESTONE | 圆石 | COBBLESTONE | 5 | -1 | -1 | 0 | 26 | +| OAK_PLANKS | 橡木木板 | OAK_PLANKS | 8 | 3 | -1 | 0 | 0 | +| BRICKS | 砖块 | BRICKS | 12 | -1 | -1 | 0 | 0 | +| GLASS | 玻璃 | GLASS | 15 | 6 | -1 | 0 | 0 | +| QUARTZ_BLOCK | 石英块 | QUARTZ_BLOCK | 20 | 8 | -1 | 0 | 0 | +| AMETHYST_BLOCK | 紫水晶 | AMETHYST_BLOCK | 10 | 0 | -1 | 0 | 999 | +| OCHRE_FROGLIGHT | 赭黄蛙鸣灯 | OCHRE_FROGLIGHT | 100 | 0 | -1 | 0 | 1000 | + +``` + +## 2.3 商城部分介绍 +1. 自助退款,买多买错,不用慌,一件退单很安心
+2. 购物车,买的多怕买错,先加购物车,后续统一结算
+3. 害怕订单找不到?一键查看历史订单
+ ![](img/shop4.png) +4. 玩家要耍赖皮,拿着小票来找麻烦怎么办?退款订单自动贴标,拒绝白嫖 +![](img/shop7.png)
+![](img/shop6.png) +5. 想查看玩家消费记录?小手一点,明明白白
+![](img/shop5.png) + + + + +## 2.4 打印流水部分介绍 +1. 管理员怀疑某个玩家刷钱了,怎么办?/printer 玩家名,一检查流水,很安心。很多人都刷了怎么办?/printer不加参数,一键导出全服流水,查他个底朝天
+2. 忘记删除怎么办?插件每分钟自动检测,旧文件超过24小时,自动删 + + + +## 3. 指令参数及介绍 + +- Sdf1_login 主指令
+- Sdf1_login reload 重载配置文件
+- reg 注册账号
+- login 登录账号
+- l 登录账号
+- sdf1_login del 删除玩家账号
+- sdf1_login radio 停止播放广播
+- sdf1_login radio reload 重载广播并重新打包资源包
+- menu 开/关 是否开启菜单
+- printer 打印流水数据,支持不带参数&带玩家名为参数
+- sdf1_login printer 作用同printer
+- sdf1_login shop 商城 +- protect 区域防护 + + + +## 4. 自定义菜单物品 +- 打开主界面,选择"我的"进入
+![](img/menu1.png) +- 将你的自定义物品与雪球交换
+![](img/menu2.png) +- 随后系统会给你重新发放菜单,并且此时图标就是你的自定义物品了 + +## 5. pvp战神榜 +- 通过此功能,你可以在服务器里制作大服务器同款的击杀排行榜,而且能精准定位指定区域
+![](img/pvp1.png) +- 并且有着网游同款的击杀通报
+![](img/pvp3.png) +- 播报逻辑是:
+- 3杀
+- 5杀
+- 5杀以上每杀一次都是一条播报
+- 终结者通报,触发连杀的玩家被终结也会有全服通报 + +## 6. 区域防护 +- 通过此功能,您可在服务器里需要防护的地方(比如展示框不想被玩家随便破坏、主城不准玩家袭击等)加以防护。
+- 还能够保护指定区域的玩家不受任何伤害。
+ +| 指令 | 参数1| 参数2 | 参数3 | 结果 | +| :---: | :---: | :---: | :---: | :---: | +| protect | 无 | 无 | 无 | 帮助信息 | +| protect | add | 区域名 | 玩家名 | 加白指定玩家到区域 | +| protect | add | 玩家名 | 无| 加白指定玩家为全局 | +| protect | additem | 区域名 | 物品名 | 加黑指定物品到区域 | +| protect | additem | 物品名 | 无 | 加黑指定物品到全局 | +| protect | addname | 区域名 | 名字 | 加白指定生物名字到区域和平模式白名单 | +| protect | addplayer | 区域名 | 玩家名字 | 加白指定玩家到区域强制模式豁免名单 | +| protect | remove | 玩家名字 | 无 | 移除玩家为全白 | +| protect | remove | 区域名 | 玩家名 | 移除玩家为区白 | +| protect | removeitem | 物品名 | 无 | 删除指定物品为全黑 +| protect | removeitem | 区域名 | 物品名 | 删除指定物品为区黑 +| protect | removename | 区域名 | 名字 | 删除指定名字为区域和平模式白名单 | +| protect | removeplayer | 区域名 | 名字 | 删除指定玩家为强制模式豁免名单 | +| protect | 参数不分顺序 | 区名可以写这里也可以写后面 | 玩家名可以写这里也可以写前面 | 完美的宝宝化 | + +示范: +- `/protect add 末地保护区 players` 添加players玩家到末地保护区
+- `/protect removeitem 主城区 TNT` 从主城删了TNT这个黑名单物品
+- `/protect 筑城区 addwhite zhangsan` 把zhangsan加白到主城区的模式豁免名单。
+- 所有参数`任意排序`,`区域名字`随便填写,强大的`兜底`会自动识别"宝宝画板",你可以放心、大胆的乱写 + + +| 参数 | 效果 | +| :---: | :---: | +| 和平模式 | 所有敌对生物全部进虚空 | +| 禁止袭击 | 立即清理玩家身上的袭击效果,传灾厄村民进虚空 | +| 清除XX效果 | 立即清除玩家身上的效果 +| 给与xx效果 | 根据配置文件,给予玩家指定等级、时长的效果 + +## 生物属性对照表 +| 中文名称 | 类型 | 附加属性 | +| :---: | :----: | :----: | +| 溺尸 | 近战/远程 | 亡灵、水生 | +| 骷髅 | 远程 | 亡灵 | +| 流浪者 | 远程 | 亡灵、寒冷群系变种 | +| 沼骸 | 远程 | 亡灵、沼泽变种 | +| 焦骸 | 远程 | 亡灵、沙漠变种、免疫阳光 | +| 掠夺者 | 远程 | 灾厄村民、袭击成员 | +| 卫道士 | 近战 | 灾厄村民、袭击成员 | +| 幻术师 | 远程 | 灾厄村民、Java专属、隐身分身 | +| 旋风人 | 远程 | 试炼密室生成、免疫弹射物、强击退 | +| 监守者 | 近战/声波远程 | 深暗之域、附带盲视效果 | +| 远古守卫者 | 近战/激光远程 | 海底神殿、水下、附加挖掘疲劳 | +| 守卫者 | 近战/激光远程 | 海底神殿、水下 | + + +配置文件解析:
+- 支持注释格式:`Java的//单行注释`、`yml的#单行注释`、`HTML的注释`和`Java/* */块注释` +```txt +# 区域: 主城区 +起点: world,-130,-713 +终点: world,-427,-560 +高度范围: 0-255 + +没收物品: 蘑菇,毒蘑菇 #没收哪些物品(末地推荐这两个) + // 禁止放置方块 #保护区内,非区白/全白玩家禁止放置方块 +// 禁止破坏方块 # 同上 +禁止PVP # 玩家之间不能PVP +禁止摔伤 # 摔落无法造成任何伤害 +禁止饥饿 # 不会消耗饱食度 +禁止一切伤害 # 任何怪物都无法造成伤害 +# 禁止丢弃物品 +# 禁止末影珍珠 +# 禁止使用弓箭 +# 禁止骑乘 +// 和平模式 // 所有的敌对生物全部传送进虚空 +禁止袭击 +禁止交互展示框 # 核心,任何玩家不能触碰展示框。区白/全白玩家除外 +// 清除所有效果 # 清理所有负面效果 +效果 村庄英雄 255 999999 # 给予玩家效果。格式为: 效果 效果名字 等级(最高255) 时间 +效果 生命提升 255 999999 +效果 抗性提升 255 999999 +效果 抗火 255 999999 +# 禁止药水 +禁止爆炸 +禁止使用物品: TNT +# 效果: 夜视 1 999 +进入提示: 欢迎来到保护区 # 是否开启进入提示,即自定义提示 +离开提示: 已离开保护区 + 没收提示: 你的违禁品已被没收 + 通报批评: §c§l{player} §f在{area}违规携带违禁品,被没收 §e{count}§f件! # 通报违禁品。{player} 玩家名 {count}数量 {area}区域 +# 惩罚命令: ban {player} 60s 违规 + + +``` + +## 运行截图 + +主界面 +![](img/login.png) + +邀请数据 +![](img/login2.png) + +积分商店 +![](img/login3.png) + +任务中心 +![](img/login4.png) + +垃圾箱 +![](img/login5.png) + +管理员面板 +![](img/login6.png) + +管理面板-用户管理 +![](img/login7.png) + + +工单中心 +![](img/login8.png) + +工单管理 +![](img/login9.png) + +我的工单 +![](img/login10.png) + + +提交工单 +![](img/login11.png) + +服务商列表 +![](img/login12.png) + +服务商抢单大厅 +![](img/login13.png) + +服务商-接单界面 +![](img/login14.png) + +管理员-工单详情页面 +![](img/login15.png) + +商城主界面 +![](img/shop1.png) + +商城购物车 +![](img/cart1.png) + +商城退单 +![](img/shop2.png) + + +## 4. 下载地址 +- GitHub:[https://github.com/ypsdf1/sdf1_login](https://github.com/ypsdf1/sdf1_login) +- Gitee:[https://gitee.com/nihaoshidifu/sdf1_login](https://gitee.com/nihaoshidifu/sdf1_login) diff --git a/backup/img/cart1.png b/backup/img/cart1.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9a1c669bb1b7b4d0422cb53bb15ade1deb0ba4 GIT binary patch literal 99972 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ΠKp*{IVRQZU%*n%zk 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 literal 0 HcmV?d00001 diff --git a/backup/img/login.png b/backup/img/login.png new file mode 100644 index 0000000000000000000000000000000000000000..b9433b380920f2d82785929f82a46819073a2dd4 GIT binary patch literal 84601 zcmXuL1yEbh`#l^ycyPDi?ry=|-QB$uD^78DDNtMs#fobw#ogVtxI4Uj{=fITNhXuM zcXsyfK6{>X&h8{iRapiVi4X|@0HDgrN~!|@FhBqRN(Bi0QDa>a(E0I&=qjt{4gjF_ z{r7=NV?-kY0LTGyl46?PIVYW79n>=Zv`^1kT6v#mY_B>~WH7KHnDY3+AXByzeMtjZ zc%q$yd!M`I%;rr0hw6ox1${1=~iSYK33Wz%fS9xxDg7%ayTT_pESsHk--XJ$?7 z);I8l;kMQ6tdkST`uREOZ1!lzNXy8m?B7avWcGrS1ONhnK$Tm9c<$vI?@u)cqJ+BR4gup5pR2KpKAI*}P~!%-iUrMXK+Og1~; zQWgZ+jpxxqHoXYOEk&7|?&+3`LJ&=bg5*zXx7!7UoMVFw^!AlOTLl1s-lywfOboRO z1yULlc9rNwE-*9(fTtn$0gB>D?`uCH=?t4t2|a=geDL$pKwitTBK`NTTjWsq7A?L7 z6Jw>x;twk{7Ew%g7Jp!=O@gV9OBn$mV6fz@y|q~vfdU{E?^|S5qnqQ}khAWeNJ=n9 z3)IJ`1=FeNUWp2WuS9=cXl&W1jZP1R1Aygn5oEI`^w|9fP_N{sEjiPAn>B;Q(C-HE z2N=aLvZlyJSFF?`8bE*7>XtMZ@@kY-%{myP#<$jpp)AiH6C_9|9VjeY$~pZ)_wO+X z0>th~8M%#%WHR$0C@5ZW^Q6efzC81m*9C(eq8_4$0guMvM|ye{0FYYLX!2gn!(CXS zQUynroK+h3Yu8~Kr)0W~6iu4s_c9=4-X6k8jhG}yt?hEiHS1O+2b?+IPJ*G6gnOL{};^W1j#{jd?<_0MxlnxYjg!Kq8W6`Z(;N$L}^f zZF~m+1Q8LC|WL&Oem+aK@YKjaD!&Xu!^WG%>V^)SZGOl&CKV1`zwdg zCYq`-A)JToj?tO81cV5RFceYV;9f5eka$pP$0imAh~jb-1^`ImfDP>!d`;LOiMkHF z!=yC~6racrFtEJ2GOpmJ&i)2EoR@@!)l_dJs%|l$#J1Xs`T0jmuMYP$W8Td+}MZ%y5WiUYj zfzWVByJ%i9q?j)0zyK8~R}=TKWkSH8>{O6?J_Hh6UhP4Q1O=j^3$`3AY`6NYqExVh z71VP~{_eP%`Cxz*l*w`y6vk7H0*3^k`uq(05zDX4mwprq0d6X|5K?1Ua%^Zx33CnZ zU+i13`v#G^hnR!!m^(}gczSPe(V=^-b_jh6x~nqs2FYbX1w@!uo48%t437LUVp{ji zk`}0in{uQ9>yTMSq*$AxgN>lbRRwBljJ;huRgN$!c_gfG04SGRAl3=Sdv@CjOH((taBFY-;fs9}1 zinXeC_)y=s8p6b_DJ{8df76KBnj)!-NXKFEHjs!~gS+)}sG-u;OFf*|j=aZs!87JN z^|4*ritQg<9$AZ>pmvX*WGRr_%S%jTq9VC>J7NROYe4es5t*hLl2;HfoEIMWV#8a_ zL;34Q->VQ!usYI_FO~TLn`gf+(f4+bZN%o(Fza&pwlH76o|x>7DLNrN!b5|SKSJe} zCpoOzXG!!G+sX0yjRYLd5q<1)vHw#r5l#2^X+2&dpwkI0Dq% z4Omm0n++sP=i`q-qDG)d+$sa9$W&Eej{@NDUK~G!m<3nV)_~9fC55U>LNhYWAma>o zwVkqN{H0;n?ZTo5hPk(XB;lJL|MeHZVFoS$t`H=SWLAY?+>haDh8+ounOet#!U1J# zp^HQvwt1N*E=wOY_>_bf`EvF92MR_3kx*iYj`V#oFkff)rIe7LA1qqW#@*=$$IOZx zkd!PAM%scE9XxKMQY(1^?P7K4ahME-R(+Bs2agr=Q&em{B;%P|r7LS zT&nZ}7uz$yAqoh04b$BXg@h3$6tff*i2?$qRshg~S~X#x@b+beFx1V|L3HqMy}S-z zVQG&zebS4VgFO^N4Dz=p2F9A%0*T$P;s^s|ybSXgSceXEUF0X%c%s~TuMO42(`bAM zu8V{4WN`6iDUgdEQO9@>JT;*q*x^huH8MJt2?i$O1)b*XsT4$0-ZCze%i9e*_Bt~8U+nQ(?N66>@Z@~N|aUv~DA;fWy) zIgmi>L4&D_ZFb+1O6xVZd0#Yzda+=Xq!jmzPnZds@u@<|$h&qhYw>%WQlXQZQ+G*w z(aBPL@B_>61AgO(Nk5F0zKEPQlfBeFU2V&Nx~aDiSM!CqySfGINyw`6>@Il_bse!_ zNrm{Bcs=y#*OT>F{t;-Eimxe0_MIP?u{WmEMQbKb&rR{h;d_ch1E^I)$h-if#qxsk z0TZ=qlLH(R!>`ELsA-SmV2m_vWbAo;C#CWVE`hm_5{*7|d

aVQMOLn91-UvvlO! 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 literal 0 HcmV?d00001 diff --git a/backup/img/login1.png b/backup/img/login1.png new file mode 100644 index 0000000000000000000000000000000000000000..4f54df955d422591c9f65cc481373babcfe5d174 GIT binary patch literal 108432 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 DwpR=U literal 0 HcmV?d00001 diff --git a/backup/img/login10.png b/backup/img/login10.png new file mode 100644 index 0000000000000000000000000000000000000000..6b083c263a3684da0e94a668fe61ef777494ebb9 GIT binary patch literal 96574 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

f#2n3zuSUApIPm+#oXz= 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

jlauT14 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 literal 0 HcmV?d00001 diff --git a/backup/img/login11.png b/backup/img/login11.png new file mode 100644 index 0000000000000000000000000000000000000000..c1220c1587bf1c3db78dbc7d8a069ffa300ce507 GIT binary patch literal 89730 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 i7gxM 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 ZWfTYmno)P^!FvE9E%8mfR@5-~{{us1z=i+- literal 0 HcmV?d00001 diff --git a/backup/img/login14.png b/backup/img/login14.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4f091e05072c270576102fa1ba4d49cf8e030b GIT binary patch literal 46201 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 literal 0 HcmV?d00001 diff --git a/backup/img/login2.png b/backup/img/login2.png new file mode 100644 index 0000000000000000000000000000000000000000..03dc32c2d1b7bdad10f2bcbf89d36af28b4781c8 GIT binary patch literal 73963 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 J5^2Nt{|6$mf~Eif literal 0 HcmV?d00001 diff --git a/backup/img/login3.png b/backup/img/login3.png new file mode 100644 index 0000000000000000000000000000000000000000..b12a88b585309ba34e65fc265fc797236338aa4e GIT binary patch literal 139266 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 literal 0 HcmV?d00001 diff --git a/backup/img/login4.png b/backup/img/login4.png new file mode 100644 index 0000000000000000000000000000000000000000..ed32c554fc4660ad41af2a74331d42232822b231 GIT binary patch literal 170338 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 literal 0 HcmV?d00001 diff --git a/backup/img/login5.png b/backup/img/login5.png new file mode 100644 index 0000000000000000000000000000000000000000..fa8fd67166420d290908cc74db14656aa9a310a5 GIT binary patch literal 198550 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

W*xMlQVYgT4<&+Ax8wpYfy3I60puhVZ@~smWoq zeMUeu3=(b+4(G1nY5HS@hds^rYC~*awcjU%*>hXFpAOrMU8lGly}^+11!xdk`K|2p zO&WqM;0c)jb}-`4-n#iPFVEUN31i@3&Sf*W1~-qZX%QwlMO;c1iV^?y&vwetfg_aX zf@ZQ7VtHn|Mj!>T$fUwd`)+t^?|Vd!fZzSxNWi1#Cn+YG@7oQl--oMEdUn1iFbEo6 z!Kr_Gm&S6}EcoOZ?0?5cU?OyPO=NC6%Up5VPRdr>Z}1PekyqCUTZhMD8A8SnCd!yE zl^10OVmGu01D22FcR`H3FRX$4fWQmV-j^!QznR?EP!JtYeXyu@GfZ6y>ur&Q>#|>v z@T{x&{DkQi_VRFZYgxPO8$D&i?+j&|Ga7Z67i`|Jm48HmhN}z zyVW>~ULAC0G$QnSsU=iSkJN~4c4?fbdT6$zWu0uvkrpCN#NA^!qi0deWNiEWbqkcc z{Y)BHyZxN2{5fj;?kFPk***i-Ykbd-t8MK%w7>Ei5twLM4S!O-)@!Nuz2c}-=^9s< z&?-qMsN>!+|FNU#s79K=fC!`P)qM^GM(w(b0OY<O2Ps@Q(IF0S%y~ zT@e1~UZ59wm#N&V_3&*+I$2$%c9&_@o!uOqdzay^R@y&!h{25I*tMkdYj{f{?1M1( zuVZGi5>;Qw6+@fCsAxCpow%eQI@#Dub8qpsAI$?*B)Zu^LjV=csrqgGpLhJ+Rl=8T zn-5jK95g)P-4%q#W*7J7%9}JW8|eAn^EjkDDKs{qxk zKHxz89c%a*e{+_6;~N;{@aY-IpMAIP@*H3KdMi7ehQTRZCW9s}L*E?5Qn=p8{L4El z;C$p$>T>ruKQLhRri%sWumLoH=%}&4{2rqd-J#5xPipLTlKV>^cIjwYH&-{^zKAd2 zc4GV0ocwvT^+9aKF<=i{jBo_=I-Z4a1ofA7*2h`GM{}2n(Bp$I{lsh zMoqTZvrC*d`5>+n`3~*)d25}gCyhYfKkKZl6dmcYv-7rBxbGX815?&AS$i-Q)SL(Z zivm(JGiT&`DPar;tRHXS1pZXIm!eqcQmhtyXwUWlR@6*@(!wM}-)3bxL?~NC33a8V zrC8tOB=3|}i8WwjS&q@D3s=;WK+bOB);3k)mU*6(_0iJdLg#Q_(#HB=@lJ349w$8O zqHXg)rn%TUTX`w*0;YH?R4mx-#_^(C+6N?46Um1>kuZGZ%Lsj#+HNn%&9JYS=04zK zY}jl2-K{7`u|;29d&|I{rCC^$=Cc4jU_o?7|GBQnKC6^p!dtDDfA3c(P8}uuC4IPoTI?Wb9&F~=3@b)bX7h1DYlU^M%}|{ z<<>FiH^4t>wC4-&E;znxHp3t=BjfpQR!+-LoOxz4T3OgF++5?0pzqxdwg`$%wQ;F;wMM?XF9n80P3^%6XsYIg+(9H?9NgLCEdX z>9ry5n40DR&c>pQ3f+Z%x<7M6E`8ka(9;6oPxIW(6v$hcIjd=AK^<2$UhahWgviL6 zibnV5!8H}S+%w!Q=~91n8Q+lH%uSWNx&^(wmu`95d;HHUGbS&8I`soXwSD{Le`zl@ zxZ^aU3bGjb_5*Pv(jPU10w#<+rkXjmw+0Ho3WUyV(0#WIr3}sc$AoCY|MRP>R!=+x z)SM~l#0EMX=~Q_^vJxb`%9x62#QSZvGX$1kBrnF8UGi55q)pdvLcz5n@dq^jD4;2m zHBMN*c`Mkhj>ilX;ui^Y@TuNzm{pD%HMAWJvcyslyCbU~|2mGQ2j1B`)l^hf1$8LU zv>bEib;K|1ZO(oFYvFh%X6N(W&$2x*XZfgwJoCM3XwQno`VQV2e@W-hmzgWAQl8&~ zNtpo@KUl;FAoh3y@37wuJ2x*M1Fjks0Rkuk#M}3h?>>1y1-<-UA9ZM7_XEgHge5fX z_MD;WUARTCQJk&qUA<51-d}_-^Vu#Qji36Di47@30X>(Xci20@*0{$|!0!>@El``h z)13MxcQ;f!(61jzbgd_!QJuw2Vu$v4L=TR(%Qb&LpO6xeKy=a69-}2OIqVCgtzTXP z+tCE1z(ebqLnlI2Nm;6DWk7!J zy7vh^u!1yjkLUAr=JRdj)o$1L?V+{vIiNTHcx)2=k~EyMjIlH5B0cb#ym`MYsBNRxZoMy8Qhdz&_)y8p}G z+=%t9b&XYSFm~FI?sW_^ z0+?&wvXqSKo@z6>`(fl(0CYV|wvVEe`X{&L2a=M0G1egxH#w!_7@ zWERxaf^AghVZ&2ali4uh$c4yTb*sE=@tI1~d5CY5exOI`AED9YsYyXV-DG4jO{Z(K zaZ#vw5yBro+tf13vQZv_P>2XYqq2co_`5S2<1#H;K=j5hF(-7RN~^)Fpa5Y*78+Iw z6{l1c_vkWqZ0dwX@>CCXmDy6UPP4|qdFdhQJ_Knb`39_Xz}oO;3-U%aX5}}kU|3W8 z=S}m+74h9Q>D@SkY5%W8K3j%rzc*swCwy# zJvHqEyxtAbfKP(T^iXg*ZTS+XV5vTz?&ky3B^vP@=rF26QQ1N7TE4WDJ)AnSSh%XK zB&FyMT>Lcy(_*$HYRW_jYD74%Yr3|E6mGzs^Sw0TuNrYi@Z>d_;~fu9Qb4 z6L}o_)rfqHDm8I`WAN#A5h$4XHY8{wnDw(v#2`^D+V`4OB*Z5xhBt?>>5pdnn^hZW zOEV#1h9);`eaPCpC>l2Vn38S6y&p`Cjl88;BIKz9xy12@*=RReKK|yiG-Dpp;A;d~ zBC1z(IUM(_zP7L=P5jc{&M!UeGXB^YvFW{K?7hVRpC8Ae>mWFK?c;x+U&!;Fa?O8P zEF){iPX|mkH@)(H2|V@v2$Sx3d_Kry|Rjx22Qhfsxk_X$#2|0}pYN_~d z`uSUiA+kQgGFI(HvsY9z8;La5&D8Kr%X45#&X%;uI6t2Mp$i>O30(!~mV`t@+&aTh zR8yk(ZE?8JKqXl|?eQSvW-IVD~ z2^MpUJEY}d;JsvBYeIskb8YCCRXuo5E(_&424u86)DgzuT-vsg+~nyqpV?-L07Gup zR324R-9+)K>XG=p!+ItD6>L#>7xNlX(q9*oH@hWtj#0!@N{%K#lX)zKohh1Q&eB}B zYpZg+K0pUlqq^qbawE2y6wAr~s+Ayc4{7!TrWDrXssMd~%j44WyHs_uGNLJhQu5u? zS+*t>QJ2k^da@WP^2~)3C#&YMhIEq56RXMhfGA`BVIo=$0To~qD;g40g94XJUQHwf zlI}%9R$g;u20ER6Q(;ll6o&Bs1Q76FG?jUbBaE7EYX`=IN0P(&a<6w zCe0tPJpx#CG${*jyzEsWS*VPP{xrrem`0?Ht!l-yHXcHRCriBk_+wjn-qUxFtC!hn z7(AbBe(U6O;I7i2m^Rclj$mB&p?2En;Th#-ED@ZQ*Cb?L(1pWY>CGhH=WSkM>tC|u zR~@w~AbCM`lnT1q{JPNaR)cLY&)p6{dKhYNMf7r4uHt_r3NdQ*b4TP^FR^bm_{&(( zU8+Rl=xZPa08;DYvQ4q&Ui1HN=*mdyfV*fxQ}vhyoma>Ho-dK!fvK-=>@!CQM0!VA z{hNSl9azKr<}}q2_+Q9;%EUj?x7|)j(+TI(bcab%bj#rTu{61+Fn?tpcz@AY`Z2aS zIjCL?2Y|HrrIV@TP~w0>io>HgM~~zg{aeZ1i)w%h@7Lw!Wq(dO@t@b=+bG=}QR%HA=D zATMZUA2&QYga3Yak^u9XJgF1ASRO7D(W%ez4X}d(Psy;mTw~^oyO_8UA7S0G@qxk+ z1B1`ob!5ubYQ_mLA!yBx>pZdTC?1mOc;BmL6YqM%w_&rNeSfvX3>Ubgitv&JX4nOOiJ0#Z03a*<31nYxBLd_66a$ z`LxANo%_nvwxyCO%VJ-o^-lTi4)<-X7N(!ZJ|j!m&upA50`tF%lYp*Wv~ino;FnhD z|Ku2W+_UM@L%guHS`g$?1kT~9yNejOI`~%!`#j zEHoRe32oX)Bv3Np7*n!s?HCReKOFAN_PH@QX=AC_QA}FbL-MdB5}B%E8cld!b=W9X z|6;8Z(;vBUQ2v3^mLH4zRXMyTN`dW%LFH}%s&4VXo|nWaE<3>>LX01PiC(vhPnkx_ z*N&TjhgH^#mci1JZnP$OpBJ2HSVA*=L|9?X{0~S*|rxMr*R}VTW&e;mrNp zQ`;cvX<&%!qt&V@Xk)&W?mc|g|2K3QA0J~`NaHZj{iS?4zLv^!YP+ZWx@zu+w}Yde zZfdCI!iGnu^7o5w-9qBzBhnBC*mR{oSi8+nJ`WW-sagi=dX5jNh9O(2yAS_O!b)EM zr5ea>UlS!v&?40D-l86w<>?RZyXnkpjbe`|E^4idbi+N*LV(3CQ3#{x)?00>JX(Ci zOwXnJ-ojZXSscy@LUo{syWW&u-Haghk#RsZePB0vN`u6-_J_f!!Y3T=uc%`?ACzC z@AQUr-WqausuybmYI^wFh;`6=|2H~N0cP;+&HU9hW42Pu? zjrRaW)iHlzM<7V$u8CrX${omicGj4V(DIPt;z9$%bbCj0mSzY>yb$X~{#EhMr?&;g z7lx1c(}Qcgo-u8gkx$ndqn9}<)#F{fG;g@1V@|U9ho%hbUVO&h8D??2=AaAhtI?qY zcb#b9UFS5XADq(;;jdZ3xgg}n?^!$$kPO8fEUzei3ljccb~;v{$z(Wo3t)XQ+;rw9 zmQ}nogy^NJL`h=59pSK9)bO{{K z!*~v^ZX$2)M^1`}TVpfWZ5{nYZFW(cRJ5XiQ1`|}k&`D=BVy=`zzZ;5oUy0!L4CQE zdK3%D-PX=#gPrN1j&#T_#~zztC^8BA-&=q^ebYG`_{QL_Djjw4*~$>-)$o-E)Rt!z z?#kvwz$&y4>F4|Fb`}jWCP;Q@pLzmQHDis}9=vzfzcb!{9j4QooeHiRRW19Nqy3xlXOC z3--HlbHja==1X%Ae`IH+2Cu;Py|eD6tJ+34ZgUg!q#&X-(%~a-Z+r-fc>Es=<1qP# zh}!Qs87fB5+kE+ut@krWW#MYsu!@^{zuB0xT20%fvpM)oP@Nm{+oicO)nq&qzQMsI zQD#mxI<|&PeG`r@b!J!Tqn_l;gHkzT$!-^ioWR}|X`8~9pfkn6%z%M_QvOC%m9|tN zIir&y1qCHG2w6#uyO5}+rdDQcRr!x>sFcD^xCW-noTQfbXBN2>E{yImyb8Xtb2d{& z-deGPdttgetr>l68v~qDAFO?db6GteO|_$Euw+RC+F`pRwhZRF*(ytY@qC8_>lu|% zH(~Js%i{N{c=vVaH5?Y(v?*vQA}Mpd)IZEf-O51NUx{Cx(u}h;>ItYTcQW!vZ^e#% zR+PaNvIEJ;{a;Z48=kGyme;Miveza?7g;5EOyIxifcHz77jue3(w)}%D-P%M9>2G= z%!LZM3DGI8;co)Sk%(=e#?l*)JxDXDpKj$sHyAIuvO&*-gp@e3?Gy189ca-V8So$} z<4x^%8Ct*kbH@U@F~78cy)6`k@5?}GQ23joAc?o)l4V!Kpe{xLW%UD#bVJ@LT9=w$ zLLrd~JCz||w34z9n|-AY<}Us(xkk2F(+IJYu_I+u^1m$Vx<-4K&86k8HhWp`)xU8d^vQVd)& z+qw<*iEuR;i}CazztDuYw}-P?)tdOBdG=pf?VvJ>=WbG*T0qCj#yboJDE0xHdW=%N zmXPa4skq&p23n45(zdhw*=aiOBl#V2*8m)*LtIK>pru>ZpVk-94TYBoNTX zF^QJD2;T2)Q{H%p(iy)uJGv>IodL(Z_~wMw<4mDn8Wh%){K}tFoAST+Dn|G!p`qpE zE8|$9I&weqrsqUDA6alHS${3;EVq$sBs5gxsvAKhXNt`)$18O>b+*|J9HFXgk)DAN03TLK2#&fLl13#-2vX%-treUmJ= zl&%))PIx_eyq->NI$@cR{Kk_v=)_&rr{y{sw^?{F`@OnhNt{QN#o9152wv16bQ#mPe zVUV#%4Qnjw^jLV?+{+Ss3jo_^LWT3dT;UxttgsG11nvL=TmZM%-7k9Tpdz~qJ39+1 zh7gh^3R8lRsyffJyAMI5B>+OrCTUciXU}=}lrNqMggcU3vT|v1R{#)qbN6A?d0(h@ zSHML`x~RBEi)LnUnJf*1np?|YnDcDsH>Q|NIjg0tKTdE_u^83OP2ka7gC!?5j3Zz% z(n~@f4`CwUZmMCLhh}0%v$`+CcBM(Py1g^$cQOB71-UW_4qbe!g1yK;-j91hwIyuK zYpF??US@vr+PrcUe--8&%hE>schUQM+kko+xu~P@P&Af{8jV$o?QZU9M3qzPC{#O4 z>k9i`7q8_-rfcbqe7WR=*AvF;p{TmScs0NJ#c}^2fA7ouy zz^p99bM?I|?$A$9Wk5=9^oL z^G#MOa*UVJEHj}PCpzh-Q>%AUSQWGu7nf?v>O?Om+RCkIp&qx@)!NEk*9pmfnPFK5 ziziK9;H-5v>zpsoUFLa_n)+qcon6%P#gAmU*H;&y`D^!r`Vkp%{pa#mef0^iCydua z6Na0KSIcF!o@x(8*DDdP)B1XHcs*k0PIz4!lH+)Jc`_eClsn$qc(>HhjScnwsytX` zw&9?gZ|p7*vaLkt^1)4&vxJ9}P#!Yaw?Yxc|(%i_&fPprRtO z+ohnPa?}!(<_3U5Qt}1c?Vt*a2`Et@stO>+xEY2iO^%X^nsre6abj^y$fD9x`y1dK z6M;EaWoWHo5|gPYuvOKRBB^lIFcAVI!iyTRR8Y>wXv-0TLPBVkId?eJ0c8?((6aT! z%|Lfkh=jtK&uLkZf|Jn*p-)ht#nVGhKD@gS()6 zHh}e>uIQFv8|D5-vXhguS*^)=kWP@RegKOYc*5)D!0Qo6yODSay!%jePAm^e)GH0I z)B1Wscs+90PIw&|lzZS+hUAK0`(CNER(38rwJt*qRONtoR8g!X1@tBNl(bUAx{Qg) z>wwK+Kf)sm6fb<{(+#tbQc_irQ=CD#VHk8j&y^Mum}44(7ABV}goKb;F%il9_A|G) z4nKb}84@6w_X0|!k=6KJM|G0y)vCT)4h|$Zobz1t)iK6#4Ez18L1|m}v>7+MDG8#6 zF~->;A}Of5i+f5jaTo=xxf`>=#LSTP5N7GXbpR{^b4nYE=5*W10Wng|4HO|E07voJ z9$|OCN9#4Ll^yP-rY6Vab5$8#vk!3*BfHJIdLQQ#t?qeSmDR(yEbYZ1VKr@gzYPsd z`HcVQKln@S_%omW=k2)n?eO%v`bDp9*K8?Amk#H{17~V)+g>NfmCq8J>ubq+Uo03k z@m5PT=1o#{2l-$8{hxmz6yN`~UyntuE*8Ktu5GTCt;$b$y`*^EzP0RY-}sH+coeT6 z{^1`!dY<*GaRc#s+gsoK_;mGV_7+-jGw?dCuiT#G`uf|SeZD>ax&P#c+VL-SeSZEY z|HksiSN_p<_|Jd#@3-SW^MSA9%8-@2P&I)kyebt}4Pyr2h?ZhXIVN~vet=;J-XeLH z^|#zAlmmyf%^23t#>IZ$e!XW89*IA6+duz*he6|91|xvj@26o=n&zKG`VY%7c^T#-bRV-UJqau)+B zChCO3J*AqcDf74PqyWanSbnwE&+P*pekW1Y1E8C$2WpjiwDPW9p0ZdF)^F-$lB?2k zNY%QJm-OWKqVCvp9zb`J8#n z^;0>Vd(riv<~HB`U5j@?H7jh6h=y?p(-ccMdFJc@5mAwx`zGh|Hlqk*N|}KRL8dw7 z9N!^?l%i5nax)VOOG+Lo-jtw4K6~fZ-4|yofj3aqm|}LuNDe6)JEH2Bt8m*U3>UVZ zVVWJ31ZeI#LBL!<5gFhvD)SuG0ySn~$&vv~5W1>}yO>$YTxljE6nBTa!94^Q!abUg z;yKYyB57A=5HA7OIR~=^wEOzEW&SSt-+lEsHVJOk7-3iP}VJ1zVr ziR~VgmLK|ahwJ7c`??Hml^R!FI~d<1RGjcyKkKE%>;AobD)G8BM6V>gPV4J>@w(oQ zb=)7CqUnTJRbF1h^ZRZea1AO%OHbo98mzFxWpEC-y=+`|v^4A4mkvcSomoVEybfEu zcs}hv4*($CbAUgx!;u6jLIE&_5zx)JdG^j3A$c6#-Q3J9rDT?prQ~MrmjC0)-OQ2~ zKSke95u`cFE2?`R0W+Ax;z*1!1~bl4rWx~uY40&IMu|yM3g%<-!F(IjU?wRIz@3zC z?RPTmA|&V4q$Lpg!12CMWB1=C2`!>URYEUQ)B6iiRMhZ(0yU-UvKAK?4xk1%Rj-j= z2E5iCh>N@Ffuvaa*fz^L)$2Y|eT0{*fmU0f#aON#n^$5aJ0X=1yRK_zYOdGrhfZ=` z5wW`Qgx6|dUUIzd*`8LsxK1PTO2g~4zOEOq>+DP}HnzPZ| z0frDl;AYep@9v!x5fCXQ4crXFG|%oHRAWpjCRNQ7L?t9kxxDI79itRi!qRU}#j97UN~LB-OjYSWb5 z4XR9ez>8-nA?_g6jUr0HFocvWrgVNc4k3sHz`?^*mXC%;`7c{0iPF}t7G51Tz3}v% zz?UbIFZDOeGrb9COGc%9rrzN|EtllH|58YC@ptL{Hu}1jeQUjSuf=v&)OktO+D>qx zuIq1I_hl1V^|(p7CHrQjH$U6qyu6F#ZpZI^;@eg*_fB)BheV^%} zCZZ}D?c9SCUe}9P8yq-);wOHhz5In=`7hh?N51cGwd4MpZ~owi@k+q!QR(_4U;VY{ zo{ff1ed%d^y=vFjM_>P3TQmRJcYjxV*q?dlGe`UIUw`t&cKq-E>F>@DW_v|bY0j=>G%nVg@4 z6f>KqSwyI^mEn;mNZ(i>UAbKp|I}JFsEJ~zCiix!*HqhOG3Iq!Cs)-p*Q8n%@uC4} zp3%PeB41N$Mgx}r*1ZI+LQ|!&2}(5+SB+e6&%&}Rm7=x2d@J)!tZyy*#np+acD3Rd zl6Rq2IiON_!s{CGYTiDNL)X)ZR}EK1`+%}0f_wS#x>9>O@w!)g$?WVVY(QA;xHCWH9sF&um_J zVW%o2x0}s0O`;lO5@85ov%!lm?vgn{RjFKUiIl5+Z42toMe%6vLYby0Q4i#oI*c+7 zh%paeNS+9qTaG=+SErPOgkuv%5y}7xQ;KOn%xO0BIYm_g5p>w?Cjbdh2?himQqHUm zv6Ns*sJCz53Ik<;C!XCpv*gj@`T1S|CFgXdxl-~O6v?thCbktR^ylJpi~3rs~0a;Hul=IiA@f0UyBfgI0LxX)p8h9U+uR=)1S?ru^G*Ab3DQU?GG zQ9w*{3_;zIELGb!Em8x63dkwO%{YjNnM)1YP!%$pXfcE^$Cy&?5{3|{zPp%Fk&V%Y zpmR(eQ&^HJ3_+(jXV#Tqj;6^|LLe?qQmH}_6~J>k9#tJQ?B?ihvR4v=j(0E4lNrHr z96$^jNNzU6ewxN1h-61eAz|+O{iLcwgb>az_S{iL$1#xD?Ix#hhGCA8L{@V?cB_8K z{mfnhnYe5Wbwg%Z1$kw7-L2cL%|WAS9hyp}!Lt3*GRQ8EUGmUgq1D9ythu)8H?#6GUB6CvT@PMf^SVBUr2f?6 zRe7l&-e?d}3>JT9lIE0~hDI_s&OJ&|_XXkDgmnE6cN%GxqJ0zR5^KTaC9CCsN9Ox^Z zrdvwcuOzdIDeVEOY~4UI>@SjPn5TVF?7?!1>~RS597QB0yY-Bc+{~Mll9g_iAsHCN;-Q*0Pye-U%S>dj`DW13?s+n&HvDTiWkifqPlnr15_=JO1a zfijm@$1rHJT_|6~tdq+v>zo<72FLWQT$D%8Xkw<X^De|)gUw+>Rn}=)@(ycC8uP~4u}{t zXWJ{VO1TqsbU6&|(BbejPx;YtPB}`&J=B{#n}aP3V=i($41kkcZnTaGHGJI9S? zZ$Ed42q~bes+$cPs7eS*$~;FEDObWBh%@gzyUj-DoH|iOv!mfS47cx$F~wvyjv>XI zhfx+eBC9YRV;BO}i*Um}O^~~rCvzhqq?wu9?d=&r5u}uEZEl$*ODQE2GNlw#qSGwx zrv1fkpH-L|G>B%i{`tjjx7(*=W=^3KXPfO1Lh}rL{FYNk_J4!8_lflqI-F^j=J4@3 zee7Y^(k?xaa_NF&9qu}`^3Hug4Utf8`3n#peoxL8sc=WKGIN^2h z(3c;t`@Qa^#p@wEfd<|svuydZV~Y;Q|J8_BA@Yhk#Of;oEFJ`_S2~F5wRuDy03ZRB z5<}9)KPPd?PiU6m_Q>UbxmMirwyG!0g@((YW@a(jw2$t&_=#sFT28+sakkyXZ0003 zus-|d`JjTRQ2e2e~Bty)0`pHG$FVp*&T}M1 zMd>98BZw5u%{Jrs%rb~h2{;evm(*21~! zC^vn><++&Uw4(w@cj{gyI@U)le_!|Ny8OJ$ge)s!rCC{D!ZO+J_1NRP)xoLjqIB7d zy0@+#e!}a7*TdlTI7TquckE7hoz~YqPF?!({+!m)^Y`dZ->q`|wBuDhG_UI&JVI>^ zJYF-unK@~17IN6K+3oYpxcg#ksa*0S%}g|hFfBY_`Hy+aUxS<`W=Uw{aK>mU8sKK` z@EAv-x_h!X&3nlzy%?jA<_4;Wq-bVFp#~L^IQzx<3>Po1p(F*45H#1KOVKSk)tQ|R z!JryK5K=^jEdXxDF$9e*Umr07%|C+j_^qySa!gfD#lEE>wf(5S+(v zIXI|u;3%OxwQHz>Nk@#)-Nv#!3vJ$YYlzD{=kg*fgMKZ|>&vQ1qLWXT*MiN6;_fRd zD!rx0&1I^=21#b_pfKk+%otd5 zz(tm1P5I$rR1v?pnBfqmim1vo&6~5Kg(xlFxrB&VauMjjv@fIJ?grPO8ej>xZg2M& zF~%gh)?c1;P8}r2DGwneA)wI;4?d@~9Y@Y_%;6*5xRyeB$EQq_%*-)df`T(rD|b>~T0lV;W6 ze%7%6y6A}|ffj1YDQ~Hwx_O3!P#F5Reds-{4-e(H;cw{HkF&(ydG|;Z7hLZO&$Ee0 zUZ|^HB{hRVTXxkami11QBo639DqAYH)-P25-IfZAC4TO&{KDcFW$*sdcKm~%_|1nw zCR0*g+cJjLwS*I1*Ns>FCqL4<-}~o%&!7FvE!MHm5%|Fm{jn8wHwiDI+Ih%3zx(4y z>H64r{keuiPuEw!|7&}^+Q;&y;dNSH?NKlL`Z^feqj()QZ{RMjkTr(6J@AIEuP2Gu zD)-_wG++Hwj=ZQ|7giWTiohKqprt`{M@mjv1O_vx^n6Gjxe!A1#p4iQwjS5Gqt3U| zk^xq@N#ulpUiUAUOAWgx;2}msjpcB25j9UC$TTHYp-_Y}s9ktWQA5aF;4p;!Zl>e_ zWT=LVyYo1X*;G7;?)T+aGuvI**{vbF6^aloZMPvHgiVYVQ;IR&OL}|gR`-d#bL0;TN}yO?OU&4moK z#7HGOQhouuji&sy5SuroGCWs<#cLc>j;F5+rZDqmt32WLB=D-T<$(`SrKx?7ZV+C1 zn7Q7FFVU5I!y^E{qVal|cEj*`^zPrR^>w*}_s6T{v+?!%s!>Qi7j+ZY*OSD{vC?t9 z6iuob^K2a4OPF)6r^nzmytYJCQ&#Nd?)-2#GGxPGg~f9K26F&8sKYz3Kn`OzCf6~r zmc!c31PE1^=_Sog4mxr7Fbt%N>%~5aiU>v3&EPU_wA3J>Sfog$NQ4wsq$Kk^XA`i9 zgGk9k5LFf7?qUvM$gwZ*l6x>i=T>x+c|<4_7k5w5hEdMWhVyebH=&qk*F#m|W-Fq& zeS5gL>w^yNKE)|({w&#ko;V|fFvsZTs%pu?pxKQQfKUOLrw{^_&%SQ_!tYH0MWyy+ zVFe3*b=_Wdb~HsRuw-*;(>F^l`J2<^u~tmyl8}|Bd{vIu`Ci4uDpNlCRIlQwcMAAC z6_bO^z3Hk-8{S=m4KVIzSd)l z2jW$uT}xlv`tpvto4vlC99}&*kNn=Q$)&m`n{l&vo-(tEsPVWCK!StFVa?t{isir$ z^XxUIWX=g~8q-T6B}dI;-r|^Z#gocPX8y26xuiNh%EOC}FeMYEo5LmB&U1o55mK0v z&2tjTDmodr8l$O-nYHqLIcp$=B4UORG>=6da1n_yreq=zWs1q&h9QKY^JKe=C_>d7 zA6-&yXBRJn;C(Wu9yeWFDpjCvL|Hsl>8ydzCKGb(6WWs-6k)s z#m#w1YE`xKEtc%@e4)x5sdAOKQe0(v_pUHpY$K>XG|QiD2g{4T?GF)DKO0 zvA28Iy9C>R+5PwNTU{>fj?2cfcm>sWp744}@oN3K^p$H4TsIA`<-2_!U0WaicSzSO z8Lx+FHw>?ifPfJI03ZNKL_t(X@BhtOUzhs#0K6*S)>yQ%i0X*6HFZPR*OSDH$G_88 zZLEsJ+&{Q)hEO|EQITd`zFdykGsCk!-KIp=({{duIV3sj3Oi3kk|k~>|Hs-*h%v>K zs_L*%A5=BQk|{Ckwlt_nf))HikQj|7?w&(N9NeA_cVF14$~XoJ=BzqH80I(wxOKMOPxEf?A?TpN;X@cM_7{1uhM?KX44+j6 z;2iS-D56*MLae!g*-y@H4&}Am1jueOtk&fEru74xncyQITKt3_#&3iE##L9>ufDH- zq~_N;a${QoY`$zwq&Uyn1P0^vxe?$G6|{mUi5GNXfnYrItcY z-3E%rq;w+Hs(&ZEE_cEE!E3d*^11j=ez;xT`{(_sfA=pRK8PUjp>O%tI!5d9zX5oa z-v|QFeEPpTO4rB!jX%F0`Euv`-uHFu3~{IRby{B!!0YdR%?H}?xBr8mMO8}ok1ITo z6U3kW=C7}P@W1uDpIh#Iv;YX%UrcGX?b#;RFV;W;o3kixznZttyb00$(f}V|A72H1Cgb?x|q?G14?WYOuIS)HE7}Mr#gnP`@?E+Z};$=&;Xb?*VDS`Uivk?x-N>w3Cnnouj3_8c8 zD#M^u#%P)8A_>U236^rL7-o$lL5hq+$YrDrI2*T_f1T!uqzH>@6rQY9{}a}d(2ZwD zF?NfHP;#}vTtmeiXWMO7E)F4VHXG2-wxde0l+108(URqMm041>-G@`Rn-krND%EOG zXDgRg*RSw?A>N|~!$Y=ioh%y|*He;cQc&w>`}JUMv+quS4-SsmoR^npttovHZB6Mx zjjPp#C%hhnS4XM0`yWJUyR_*=>a$jq9+H2v@v5Fbhg94wyq-jRW#DzB!#4-7^~LvF zUu~d{aXL@a`s({}eR%m1GOy6;t4rvO!#6`%#mE%pSU>oFaJ=?D^j6Fs0Gug_yQ3M7 z&C?Q|yx5pS(99abNy=hjF6k4KP=#8!BuX$#BAn+qjA5SA#oaV+LpFp9Ii;jQhcQh1 z8ICXnIEE1pY;O;{yV<-8RgF^|w?m3XAVpVIQO%B)BEpgT{ag&sQp&N!Xq!!dBhE2r z0MLQwcjqvKVGM2_V^S3n0bEsub8#Xh12vEO9Oun2032kP;zWq3Ow(-csQJgoAq3T2 zBsypy3EFlun!6=qW;B7bvyB5mb)IA9l`nR?7DE%ZTB2C4I8mB0pt)NV7vXLZ`K1}# zEzuH;)fwKJmpN(?^v4LM$*#GrD5KZ@wSO142(L@5gqhX8X7XB;d`Dv!-dVP_B@UtV zopf{x^lW%p^!+b=^Mzg%ml$@`p745jyu2Ek>Xz^2#rGrzi*s}x&l)jVbHtXf+VaMi z8?XA$ed%gJ%QqRXC(>SaydJIHJiOMuJG#CO=4Y2Dr@Qr)vc6s(ym|+KtF>i26V>Ik zJ3%v}h%AN#o% z^Tn?C6dC}iN}S{7wo+V#aZVR^_v2=qrul3;#+X1_!r86y?u&a7Ci7gr8C40JdB<$F z)?yuZRh_0;Lr|f)@25y9P6aCBz^H>NhhejuVoXU@(msk3z&Hj~%^4Yo5MoL>8rt0F zX%0d2sO8i)LTcbNdr{}soTg5sXkk#f1`~6gJiQA^S+UD5l210D#hR?Cetl6dEoaMB zANk-T`fcEiEj>YXPAMDfmI<2qATVH*92Zo7l&m=H-Lrh48x$F%#3HZMt7g* z2zL=_gS_321d_RmjKd&ovBs7cnAK!IS^r5XS)r=104Zf{$`gfLk^$u?Dt8ef{PwL; z$Yd!iEGgVa1qFWJ!8ZF9maK+LgR&+oCA+qQsl! zXOrH-{^?FQTdvmfTisN0PnrWRy}6gx-Js8`z-Dfzb7dBIGA+Avmv@)_JCA<(U(M%* zh5i#>E4-Q)d?BUQQ2@)h-hcQ}$GA#j<$F_m*(#M*4aduk7nf|i_a@H4@+@AaZhto8PCof3DzV@j>+iUYz8 zy23BXlH7XDKwzGdr_BGA8g!a70)l7H47o2VgxJ|LV~pwIJkFC-Xl^;*PZ$DOEU<1! z$vmZ&4QLp{{$k1-r$x_ITL4b`s4DY5QfOv6IK+c!_N<)t^RQ7kbbyAO@1~{OWHxP= z+R7zGJ5_S>oPbgdWe^pTL5DCP3_65CRY=5?a(*9xs*<_*ngj|%7>1x{XB)tWA&>|m zsPKwTJ9YMXi&o1i%DXe(wv+m`Y~RvF!mBz7uSo`ucp^Vkf3i5m&ZVKVAE3Q)DYlT; zT(N*G-Yb1lsnudF=Ek}~RP{HCoLoPl)y(h&8@&-Ln zw}zFO4Bq28G<6H+M9n|624_jJ8` zesTWd?)+lE-E2Y#A?Q3up{Rr~aPthqE$RjUf(|O01FrLPrg_debRN6;YWC&7xM+lN zFmqOBiS}>VQ0kVy7_uH?UbEZ24%TnCcl3D(*j{z{OvnB%|Ew2&r&r!uN7YFO}RayUU@$XSDaS&lC{r#PwC5#*Ta6qR^g>L39l#FZUA17 z-sc;J*TMR_j&_sR*W(a#-FR{R!`0hdGo@6iap`DLYgOxg_4jmbwf1T!rZ{!tMyGCe z04(vlOYCwEQU)5|2$(yi__bOsznsi6rzbgQOO0aoTKq7O&_OY$6q7kb$r%K5Wn>T+ zcjw!$8AX|*&HFT-kzkx-7y^|rTwKIkcLo)4Lz-O&H-`W@v$VO3(jCK~4j;FAad*z% zeg;3gJ?wWjjN)#?7%pCjn_D_=bnl3B95+J_QuaN3knI^#v^ma02-7^>+Mez9d-F6$ z4g*0KkrdH=o7kmeq&t$CB?Fl&zHBz*#coI9Vz;~V%rkILI}W$upbT!YGW8D21+_e7 zpT);+4cj)2k4!?jBnA0;E(3r3gYQ22{D<+|5WMNIPpd6x>Cs{%1K#nDw~ymEPxBXE zc;R!O`y3CRP*nJz#xLr9 z-}lGb@&Eqkf9UA-_osLHv5LnhyvT!#czA22_g;R7fBO4=;K(~xf3H3$m)l$4`qpt6 z<~hE2_r=eD{tK6e;Ck@tJJWr^@9B5=uQk8J%kAOtI{5LlzLxeXUSB`^HGj1I;s4Gu z$NtmLeySb6=k@P6O5;E6ulbqf{NL>S5x?V2ufKHP(}~x?kN3r^2FqLvTTYl;+bA8- zW=^l%vrt=}#u9+RMpeDh8k1eTnJae)hsRjUX1DS)#irc);rSt@gi7U{Nh;gDHNiOv z^W+8~xMSGT5)D3G%oL$gR754s(adk%+M4;wPYGJSEru#>FoBfJC7uEM6$HMD0TmCKfBqV$8jLU4YuF8 z2IfkBId@L>HO#74^I< z#qL5ih;W**8H127o}cG19nS&LQf$!IF>9JnL@iUBbO`hM@Ik8a`&ZM7aHID-Y@$@U;gE9debTw?h7xx@T>pV zul?dL{?bc>SEc0rX;I+=+jfI`kQ&bFKLi;H0h?l7}q2n5bZ{Mg67{os?F+jFmZ%{P9-zw(uT_$z+w$N&0o|9`(-{bs%T z9)s>RG^}{mw24}alio}Ge1a`Iesud?Gn=e;k-B-G@X7*jvm?{F8YZ^Q>7JyNct>BD z%Su-9e9tkTC87}Sc*E;o|1ID0&98s`WmO06+`04q_kH!3zxNOR)KC5Nr$6(VYsHIo zAI(lWAu2JS%iA7=7uy%3eZyL!AFrr4=l|3CdR4BkE7A23tuU;mb59jskH5aq z%<5Naxi8Z+HL}alHT7M!Z}w9FaJ^KnZ8u%4o(l}aklh?B9Aip%MXX&@JQLYPi<2oD znnPqO4_bJC1WqIh{{RDO#G-?Cv?*I=eiEK|_#XR3V!a!SDiH|&X&2Hc{YPr?Jm7jqIkCgtLs8`(Yn&c&njxvRZwPAi&+Ek zCGUL4_x!1U>*#LS+Oy9-`(1zXUw_SW&(%}`EWxW?Uad0my-w4%{p3h8r@!QT;5}On z$g3&5nkZj9VZFIz*E9epyeeL8nV@4ss(I!<^g3Fv>!b1)s9M{Kw|4$^!r%6`w|?h$ ze#cSnu(fBNdFG=Z{m8S=K69OT(J#|=-|zKe8G0DJ8b@5rJZvVriba=$;k3TW5Wo7? z*Ar;IFm6u|URPOPH5Jd}s*;2zdJu-j#%%@(8ns$TG=gr12HFm6{t$N9boh@W>~KW?a5%#L<3CMw6Wv6E z4IKn@17V_RB%slNBqSsxfq_6nL8+t~-gNIdd+)U}{bP;i-21BDd-pw1y*ewR>g~JF z-fPO0YiH)l{4&q0Rv!n`EJr&ttPf}B-89aA5I~7ZV~$Nu@}Ku&X2Z&@uS^a5M$O1l zkVlLvQT&qTD<+P_akAc}G|DlW4BVMBTcit!;-rf+vbh>(Ra5g`5}2Z2I1mvyMhhXN z7~zRppBN!ZgX zTRrL-OgSqivgr@6<#+f`n@(=1pG9jy%XK%e0g!og?|#=i6Oa~wFMa9DzxR6|fAXm( zFJHWJ-+ixm!y8`z<~P47jbpW1eakn0^Dq74zq?U-C0peVCNXsgZbmrs#gqLGzvqYl z$*~b3&BY7S%W{KMEgRIP+E8y)%gCO;UYh*XmENcDsV6UEDC=))GpAGE@hu1A|jfnd9n0>8Y~7aYk6BSiKtIL`R5<~=CVkuX^O+=Po{d z;qI5;7`>XUFhg`({fdCfy=xS!2yWIbD_ERLRu(um8q-RW&(Z12{Z7{LH66b$EC+RrZU&_=|6U``aIXJX;_hdg#Gl z{^ehOOI|aGlOn(NT0*zE0_o(p4w^t*lc{9U z*g)FV+9BYaS0xdgitspf-!5uQ_)JeXPzF;TPeC=!#ZIVb7tI)-S57|swR>$ zvLHqUObhBG5sA2h2_BLb*o z%;Olw&Ah%6$E&AQi>#m-5RPvfw=vfNp;q7RPrX$cUHb&Q_kF*acEgL$J^QiW`$*jl zQ~AILK2Wb8d+f1s96$Mq-`}h^rLLy5;FvCXn@9Qrr8xPtkz$95w&D@mRF~sHt=H=he(-k=53k-3y~;sN zvR*d3$@7=8eKZI28=_ZNQJa#kuG(o7cdqt1-t*U~%ZrJ>Zfm(y>9yVea@p#toyHD- zSxE+W+OZH+wQYITR($NS9*l2i3RU$u0=RAOqy|`m&aNog=@hshb~;SvifEHO5Y@J( zhYDe&OaRpp0NViUxB|;uO_vGfm53 zF-{{0F=*?uGqJdcO^!`?eY;J7INRhnn}vKlp^k zn6A&BJNM8-51&7O*IgIxsSlhxcMiZjPk-{qfAr?B`Rl0OSX;x|6%Xp~|HD7}k^1-l z{;z(jUVr&F8t&Rtk33SZKl0T-R%Yc$X{&SF0?m#IU{9}iedjys z-%mdIWWD|`|Ig;1_oM&qKRt0XY^pJJ0fWz5>dzni!0*H|uDdQ=c>J-~++iwW)a!Tr&Gvrpr}#&oYxsyi_qn#7 z|EFjEO}+m0pZ$O8^;SsLqLB$m)!nLx>ukXOM-U zre@{1sib|$61KQ(dZd&Gj|hwo7aT0Csvu(K7)?@~idl$}nT5pC<7!8Tn-J$Y%yS54 zc6f9+&QpRYKYR9o8E4Ndll9a&P6*f`h8Tm0D^^z=UEN%{au_uxFaO=|0uYUd%H|{oD2hzD@dPQ-7~D#$G?$eUr9s@9A~j132FKYmDy1F=BU&uP#9q zU;X+weA735>qD=6_{^EJa?-fI^wd*VuUu)}*r8W75z>yPb{*xK-Lp9j9&HfdiksRHlD2EUT>MEk(rr$?B=3N0z?pzg3^;3GGUjFYZJV7 z5{#-%k#m9%CL(4L77<}`YDeJE68hCx<|3DN(kZ31%c=puIO|rAbqI4w!Dp6`ZX63G zT$mVC7LsJaWK<4w92TA!XU;FKT;31?OkP}=V=^e3Y7$7#oFA@U8dKo&V&SGKif{s~ z(FiwW_!~2GPD0RRag>1}S9rrH)uKhHs~M5tvJe&vA3~g`sH)5G@)o54 z03ZNKL_t*5K+cY?M8LdfRS;zG!!%ETMU74z0M5RQG>!8x_|8|=ymNDi$wnTvE$9>p zAEUAm5tggPdcDzNXidGUYwL8Sxh!V~rgF@7TCay=g$jn%*1ZH^m-(enN0i?v2%dnE z86JK0V??xAE*^aFArxHHm)+Ae=I5Skp=ZxN`|N`cKD6cQTo-1w-*-HLE%&wCvb{90 zrv^Dt;g3_8UZ>erM-a1w!`B}W?CJG9!6+wco&fOQ9S>n>AOZECoUwZ1Em%sdEdbJVueZ4MH?%;yo5_*;H6~FrJm1x<0 zykbnY#k%LO8}8~I#$UI*+==wMasJwI6}-t(;TKB(tR8>>1%7f%wzVmYWfZxC-ucGw zl(O(~m1c<$Ni{Aku6Md?H92)2N{Z>JklQ)TyW1KJx@t^pIaJRboL$aSj4`H6d_)GA zIF3Py(m}0+$(%5Vrg2^@hXxXnaH?mBgE%J8JjY>hM5L+!g*gn%fgL%|F=&iB46Zte zs#%=r%=yLDOB)t0zIoYLSWtdakPe$EgT`?>@Pqf9LXQE8F*_;3AR?=UJF^-tAFgdy z7{D}#kVxR1B$}CFoW^k`@7y#^s%na1;gS~U#QA|mc%EgB5e#BVk z^=iu+R0Ra^K|M7fjr>$a3TmtLuV*U4X{y;~=# z6H+P=Dxf;08%;^297kQBHyBcsaPN2Q5{gQSm?%a!&~$v#o9(MEu8d>Uwt;J$hGr6` zDR6)>lSY7Oo@W-;s5SB5V&zES7UE}$F{ommX5uX3Bq;3F8Asq@K#V!Eq(plt6Z-|5 zVzKg@!^yN-)s=|i9Aq)D5DQnUcFILFO|!wDIX@g;p5`eGOGg$*|2r=b% z7UQVK#6mKx*Z^4zc}kvz&PhUU^loq>UHOGzB88R!d>ANW3!Vymvmt zFinA(M3{(n)PkEUbx9{htoL^?;EB)w!HI7@@%dk?*Khe-%{S$#zxdRN&!=er&gn6K zUkayGIW=**)sJ)v@5)#UH8iaTh8Z4u(;&E~V8Z9r%t%o*pXA1t+1Uw_U1 z86CDgh<@USexwHd7qyjO*85I<>^=YEiR1l=fAo*bcc}nLl-;;8Uni0@kbvrLZA~5S zzHt?J?ZKGG+pwo0@n8xy1*z;E^cM^YX)2o&F zbzfbtfAq-@*6V-urnWw>g^ze)XxDwY=XHPU#QQHDdObgXbyIPTdAgxgy>(FBP0%$; zfZz_n-68nm5+u00+aim*LkJ;AaCg@Q7I$|Gt_y+S?j&gNyU(rn`|f+I_TQiJDi z_nAKD%%fU=s4Ud)u{U!fBjoaMSa-^bGjRfyh7wP<0qxU9KXd&)A87gY8xa&*4%!(X zQfH#$k)R1D=s*vFa3^0U@qnvpm09%ZDewU{Yd2OElacud%2Vv2Xkz+p(|>&h8;NoK zE;Ct_6WgVCai88ZoRQ(8HZp!WX5-ZQJbBx^x>!j zg~+WOvgfU#(kOrf%SA5Ss^;#TdM^VnDLv8vj=pS)E0!^vy@(ekW8TjP@nvm7##;&0V;tSdq* z5>qO#YuTkvR`)ZdOj7~f`Gep2icZBgDUIJ3=1xz0UmYBx^IvbMyh)U}F$VdLp$8`; zqt@0a-ekv6N;b~4@hg^zyk6&`FV@$3#miSDy3)62O(DyyRMKp3JXDwAI4g$ekavAM~ zS;^61+|f7MqpH%A1UkD_(&lzl zOw=ik5fGuIaGx}%D#;dSA#BS;Cy_H$z!%$X_OmOJglaAZ1tRQRCprx@)7#?a|^EA^A2J1nmwx zT|le64^sz9i-c8v%IjPTJ}P!&Lg^FP_(d60ydG@3e z+wN3e7uUu{wP#zubvp!{9bVP>B=VA=%4M`p z-M#Txy{R4a@<;BIzpwAjFz2gG_w^O5^mu>drB%GTvEqW>Jx=!fI?c{v$qP7RCZK!4 z_S$h9W{P*?FZn8k8|7kccTn?eWA{}AsN|Dtkr#b$Z)1%Zr4cWcgK9Nr(=z}04#1yuY-W(?nu_t{fbUq2_5g|T0wiwoGDhi|Ad4(Srg%Edp_`txI2OP-Tw z;gKsM0wu)JcxLC99}qmHVa0fuYhYZcXe{`uV}*_%YKa@8Nv& zHCGA709iJ$^`71gycb*Fotdy%hIW5>!56=YuQiytPIq8#o)Ou=cLLpLf|ui;S zD)JvI@GpB$*Ea(-%W7ZViT-`Pojbk!^4n-8Pb~XF^nPs^8>T@zBYJ(a4Q!$I7l%3D ze9Y_g{VVOie$>`;oW1Ff@FM;?7z;Z>_GS2xoz9_Qqv-$HQH-a7eCQ2Ty3rM4(~}okZkiz7QUt(of$P{Gy`1J#ZpA{o7+hMG@T|;A<*k3Rd24b!qgh$+G#{E*9!gUuMq}c#@Q-+{W~gqT z0-08~&8XRtC>5zFOW@0L;Cc3D4vEOfKlI)|E|UM&4p+ThgAP~CK8j7-cUrDF-SEf! z4HaREb8~2A$pcOO-DXg#HPUfL?S5S~3%WivO%IW^TztDqi;>B_G|sEDHkLExu5A zKjz!N^rY_T?|2o|bf$Aa%}(5=Q%&+`Zrpe-JY`DI06Zs@N@lekeqsIAn-vkNM z8Ms(w=YZRj1yP|AbaANV5`eli6fS$jgmA*o%E-@YxwL$KiTH<~470(CC2aJScG~#c z#C#*Riqjs3m?XEdT1CXB9A7XX5JbXl#4ln$GEqW&R7Ky>OfC?>!BW>Q==%Uch!5?~ zP^3~sGW^iK1u@|#;L7B+=*)tEUV_a&v+fwyEdLu1C!~BUc z@35;&U7cM@2l3SG+Q9SO%{!;5l&jOX?QXTNf5&WKK65KJ`3gSLdxd)ZXRkfy)$2G$ z=7zbl+O0X~NkSz72`5}pe~UV#oqF~9apSPd^zzLG`L!4fodY<7j^|GwC!D~`#qpo6 zT6_PBSNa4UzFfZjm(>=DdjFNejCgnbpZ_lJ`8eZtN4@qyJB^fTpEZhJSE?Niny!b} zua_~?YHeRT2)kjb-*u- z{J|`XX4U>k+Bx|lKVV#Mva=LYEP?_3kv~kitul1fD!hPOnq9U9cmKfYRhuu_!qH4I9J_!Tl3#c9Vf6*i7~#s|u9`xl1k!|bwvaBpTdEq?oh zwT2vNi!D*Kw`L+ggR9Vklw{W}469!oj_I_EJFl{8H%1tsmSreu_vM>3SK+hXHRs*d zJ~JGYa7|jLO4+8I>dxZbJtzIhy}OrRj3}1N#9uPwgP!rJVKpLEb+_-7gITMvU|aLe z_!0Zkqgrc<>Ftk;wK&B~0G)nhIBeHc(rSHQg@a97+q4%cg)%QM|bV5#4C9{3ItL!d_boIG%Ygp-k&33i+X> z?A=-ulc^b@jP@0D`59%nS4$_heHh0;%F7&&25M9SHK<=BBF_!=FqMKQ%lTFN{FuM* z_KjTp+jq&kpqD+hJpKB^Cv;NKX!|~&_Mf|)xf@P zM&_@S2>q_TcD2NT0fraU%>O+sOV57%wS zK*DNF6NP$2A_boV9UUK4iFUpYhbc$%Vck=~dylj#J{&n+i%C<+YNm}Kkl0p(D^Cqj zOn#EDCmf}i^4h#)wee74MXi`yWT8_)&V1V!a{so4{bkdlULN}PkpDKU26IXKb`%L4 zr>ZF9spA6lzGAx6;MnrLXi`cJQc5UMsTXZEa2KB!>y9`=M}mv|43Ir$TY|lXCHUH% zC9Mkxv-DC{MJpcO-lHU=y%CnOx`?K=(yuN!1nf#Yym0<7z2ES;^yz-e9pozUhPCxt zy?>&65sSrNlEq&Kh(4K(-Zr~DbZE_1GARw*UOcCrwaiuqdM5T$sTXs9i~~~6z#eW% zpS>{sO#%`GbWJsHI#6H7#e`nIkR`dXqX!Qc*BL&VBLP4yrpnDXih8kT%S^ND;WP!cwc)u{da|TCaxu|;-@bPS)gDlW4yToJN{ zFV5AkU$c*I`vUI+mpgkJzozE<9c1RewVS~mMXS9&P<^?=ZwA2p1hOxB4)d~MD@;xb zVgI5HC^Ayw9B<4$n0yqY$@jt4P&En`3+mSXcS?HGamVaZ)#Z8Qh2JbOb#I##XI1lYS_|)g!Qpb-6#&^bEe=)J?n3dm94rB8mVOk znTE!tR0CtKx93Iz61O5GV)o}ywaIlUhrzPwNe@CdmEBRiKXEzbQjEJz8mhdh^=u=L-fOJ_Yzh#VsYhcg z{Z<(%IX^qp!Jar|VR#^eF62vPd>7Gs4!^rurBGbSccJQr`N(a0Ly ze7XkMN45{^KR$aI$9J8~Eq5Jwl@-D^yebBB%Ch%&u*K!^Syfa;perwok^42I1QU|Ab(QNOh56*C0$`p7TE>QC zw(D{OR0rl0)2!~U?`+xSn3Z8_w`G9&o6T{vU&ow$nti3VcoQhWNZ}Q#TGX_d_DF9h8*D$1Z5^4&4(0BWCM;VMfCq=As){tyzp)NUjYbD~u+{5K+vBxio(U=Tx2f8sI(EU_E zXmAYq)?ZNb*ttbj^`uLhIvLcKW%}53vMyvJXj$QqDn@%5*2JV;|Kh^g{`o#q z!qr$#$e(>;kcF+6jyWJCr->lDmwWksp-DZ;hC&1(g2wcKMK*SDAedTNGB(Uo9mbIu zx$P4D1IK^bCh9u0*EOZ33OdUHEYLeU#SL6ZL}W7KX^bn4Ni8t4=?(HCMeDRu3;f}g z9i5z(uls~Cre4h@j`NQMJ~-zZBip?Wv;P~wM+G#)YDGFD@$Ogq^+fStBy>90#6)Z@ z6z6lqhS1@f;CrB)6bnRnq)lHCkR#sP^~G96B1S_xqRtpo_MjUevnZ&>jp!AZ>#;yA z)Ap#92j<<}QuhDc>S;vqoR%smv_-g?zoV|fTLK1ov+C~WWqqU3%Pq4L`U8h0u$+!Ph0r`87Xxh}fK zA4I7gCAb~SNY;u4m0nqF*NUW?mhINEKIGjQ_3~-xVy(<|(c;g01@uJ5`Rzn`{yv~2 zajcu|8c*A2%gvNh9qMA(0?jS1zP-CTdkWXO&-o`@oP)RaAgRqaEnH(0TY^N0i$Zw10L$|K<&nbN@UXhE-?ZbJ-eGxl=_Lw1E# zvopR+<=Ai(dQH1w)KkKoUGU#ghx;v+3(42)Wzr1Pp-cc~Fl}n;&`}W+BSM8j6HYQ= zy;Uf25z?h`7UN|O>2$79V&k2>FX*|UdVkJ znx^Ez2E+@20-vZX@*<+rN35t})?-KMl+f1*q*Hv_{JmO6-ef|j|KrGU_NVEes~SFR zsU++EKjNxzeS%+9TBmkde0A96K);FNRTxo?ph3Xbsd5DBH`3 z%&P~Rlbwi4OXSEs%6{@wahLA?XIWAv5n!1_2^^r?4jEm{no%DY)BnDJj^#ML_m(? znD?wJqU6J|7aw%mx{*HFS0Q>cu9vkc7Z- zrohq$)#G~n;;D$8@soSk*~96uPEXRY*Q2|!wy=rGEoAH)ZOxOz{R*olW3~&7wzhWXxg2OsBg$F)@0)8P(V!GS&wv`aeY2H=UQ8gjJhd{%gqD0^w*wudagB z)pJ-y%c{sOFKig@a({dq>4)o17ZrcD*Em(9=pWRupUUZ#!-8?ss5FO~DTw2Os;#!r zF57V!N1jZ?C-2_?QQqMLnrPS@w5I9jrBftZ~Yy$nx^dPfjq!~+M6vOkk4wr?U0 zChi`TPId9wgbVno@y762LK(&+>z#>OIS!QZUHVyLC+j3bEQcuU%mI(a{=6y+); ztNoiN|TuJ|*-@`}F)}vE$ixSP;h-)7092jOM_=9l3@sH9F3hPfp5OGP)kEH$?bwEspHgBr9xFcV8D~bkg&x_&`86}bCLhq9_~vTHUrTz0wx>biF0h55YZI4 zbZ|%rt_hS^IOz>EGJhptB$G?gcZkRmFGcSg(nUzAN;<&_Q0dPn6#M_gXG*!0XQv#K zP_7~}9RWxD-RbI?iOja-+B%ur&V)+h)yafrR+w`!;L#_h$jp;?`qa5Lh6(q6Bl_%j zGe;(ZRy#I(w_jVbR&mpzHQLO{S<_(l!^gMY$gZ!Rlz+|YGfsc|xwhG%F?Q!C{C8xgFol!Y5l^f;XtAdC&lUSI_qNe^^cGZ7FL8;{SB}MC&UAFf38TD(R*ir;Mg+`l>|tL z`0GE45z&{|wxA1kl%C%HtA7rrKA+D`yL|uR=l>h>68)F1=}b{;*5|{_XGL znm*m8TVW@$r!SUknK}GJHPdN3O76BwU0OWdLfDp$b^U$4A5&#}0x0U$9qQr1FsQ z@FHoP!xv9MyySjOW+Ys@7$ha3eRsy=BsbAXTo79ly(*!sHyS-0?O8XyXEtIY`v8)1 zHVIO!;t)t4tf=z=m=PvaQo~r-s9e)7X%PWoRi++-qpth26-jbheEi4Om`Vkt&Zo0b zu7DA@VB1cqgzA5?s!E(nni-bkL)-8tB*G_-C`1hp6wyz>vcDzz1E15Q1$Y->8P5FJXZG))`2cL)bvA#kuj=4%2k)xZl7my1|bFdmu3?3YA@u>ftIW2vF&ftXG?r#gL&v zOWUG<76*yS!SALYQF!4rPIb(LJHXUrQq!3%x56GTR4y;LGBU)v{aB?`s>*P|(u-YS zv3agTY0?^5~X6Zm)(2? zn`Vk)qYfYE$$VvXlNL;iVh5E~;WK)6I$~ZJ`^TsB>R9`tZyoQ{tIHTlm>_=?b-hJk z2;~14(z0}oR8qGr%{>4v$bL*JQX};D672253W&7e{PmO#A^aBwUD@03i)6S%YgwQ$ z*AMw+dbn(+tV3Hch<-qlZ#pql^JLF2D-C%=C=;Au734<1 zm|fPV%#sk0R|KhmP>}=J^)8O=BQ#K5$Cl$!?XQW$_h%7<_+ZndQ75JbzO2#lxS+3D zw#}MFaD774S*RG&azE%Iq<#P7a0P4gRo67pVkmxN!Gvk}ej6??Rv{k~L7o+zLFE;Z zwN8IQ9_got84Gf0t!EY zj)=I=J@@s?RE@U-eO8loMp@8y?j(HVIz@zxPWJ0{TbMu&9fpB2!{^@L*UMVq?4CHz zkDLEH^a1`(KGtfrxu-+|-9J?sw{Fu<;D1h95{HFFE+kgjC#5O{Rfo!_Sk&K%Z0ehx zzw&-7$daNbm__u~0``%h&sKr0ClwJfLr#i*R<}bnD-`&t47sre&6RpRxuP|aoo2Y@ z7Kl?bec}NQ`{eav;c@M8;FdNdoOjzp?8o%hk@NuOw0>JSi)DK09})my%^$I3nCovRYtvpVHtDsTQA z7iHEHM%gnsBG!`xy~a$ijt@IPcg(%e<2|zCb55T1!u);Z6;N+GQC+(vFb_(;;?=T_ zK;nct{U{|0@c+MYUw^oPwY1izF_d$&lTtrxJe8H}$HK=jS}6G}yKo}72-I3=`~Pk~ zqT!ZVsXX^{@su%GIRANrGh5|5ON(uZ)mre zk;EVs;y1%!OHZZKSVzJJ%I3_B6l8!hBLWx>$G-Q;g!82*s@|SMw$pAXZsZ8~ z5su`S?`&N`U}~KDx^f7ae5g*Uga-;z5f0Mjp%A=2TX+ilsQH56Q~wTuTBN_dQ*E95 zqVd0CS1%Y^q7av1P|E$G0Z3p(Nw+=_3f<2nF4_Abcj=dut^m9*8d!4NVfYcNlQ)j3 zU-snaCb{A;zqzPuBpCpKm@1*>FWc)lc5ojoAQ$&!ekaqhNV4yh_(`%mQCDTaFV*)3yQh5BeZb>!o551vADMF&pJhlkbZr#1grfrv37>>K#mmk1L z`&|~Gd1->-ioT>zkgn=bfv=#v+e8BE-aK}EX&FPyS&Y^-1dZ9>%UbfHL*#@)%wiHl zm(p#C^)YF(WVDF;7YiVt$!+7{hUM)GXXliiR@pKH5H%X1bzW<#a5kxZIYgI<^WEg+ z#$uv|<>T)4#ARavOl-%zLPGS?e9aww)gBxpnE+(HTFQ5X-=pd%CQ3X%GdS?*=tzz} zd+k#_m5D7wn>^HK56mlDbo==&=l-5%F?cSUs{J-m?A2mAZ((eC9PHkRz@lq%DTNv^ zrFnl9+~zHy(!BQ-FcL8l=QmihcDZcXr+8mUb)9GWdwpZUq4N9xTxrtW~EOYFiWoGzQ4l8k5@B zCDSOTGCD?Dltwl|S%&xZBrwXAenVZ`yKCap0u{ce3OosU=Whd|V~n%PQD<**<0I4( zVI=*)JOpAy+Wnu~d7YOZl0NF?iv_rDNOGEX_}-nhPv~)wDF385fl8u$Q$*X)z_h9w zq915fMe?BkQ7jT9;n$AKD~s1)`+JZHgy4ulhl?5Fa|VY-11GDTeVakoJoUU4I?Qg2 z0+V?|JaQKrB2M}uI#s6BtVfXBAstmKokorxCL*_e-7{uz+@R}lh407k} z$Y~~ZF!7_%Z)skBtoY|_*lRBQei~!(qqTJ~v}tFh3cE@2pm!pv#B^e(y%#HU`jI` z9}J9Q0qa}ophR2Re8We#M-0Vj>f^vd&j-&4A?w2!Zy7@SW~z`rH~&rZdJ7j$4Lx0w z0%ly62*^J*UhnB2c-n4Z<4TLfV^Q9-m8#IgqF;B7?u#t-$0`#O0)MfaEfeQsog>o- zTdQt_rX7_iehY({KV~-MX~iS0j#ma~Le)Ju@dOi6U3*k5&xi}Nn;^O%r#gXuhwG(x zEq@#;Hplg=AmIO6|HC!7v)9@$Oqdmc?MBP0(*{Vq}11`zoH2|}E9 zk&bcTly~89OmU$yLle~WwD7Xp=yMW7ljgLl%&}mPFLzf9=^WxB;l9pZuX!GvTG7+o za9k0Q@6nXFw~8LiE7Cdmqp$)HcN)Gv;^*YraaeNdeG{6g12HR2!SPx_o4ti!mVknt8M>+xuw2Nv|e)**W3f7NPnrFt*L)4x^H zF>=zQZePQ_9=~CsCc)vZ+TG7Xi|GyT=eO&@io?2pqoTH3KazMRoQQNT8}OVBc;wtW z=ON(ZCszbht6Idcb@d28hWQ?XSqOg8S1WM@n`D!a<%I8A?bu-!I+H8BA(4E7+qq%Npx!ekVsxqda>dVm(NP1_7LPgo>EAbeW@; zmNqOOZVIZfAsZvi;rIR^4V{J}&9wB2nx+=M4?f%A-2FnsIVmM8-RqtLaZxR#oE&KKl}45iOfW zOd_^7ISwSvP?&l|8l0EBo9IkV%#$>FhaDg7eHKrZGnLJDq*3eHr`R7q{% zvY`f$i4J}Xqcbfp@wh@&&B?;tnDT&&AmL`6YBFZSuB9wxoyipoX!^uMJqd)B#=V}x z?LF0*{)umTCRvik_X#8D)p72X3n38I6tmKEpUUrx#KoO@cL~)se1Y{hRkzJ-o2oy~ z9>Z!@mWJ6q?sT<`;?u?3xcnb-B}b-`Z5*(oDAD1U*(;z%=ZAiP6WcjZF7dDmBj)0x z^ZzZ|&X2v2=TFm8k&MUsCho<3*yck}E&E&AI3*Oy zgKywbt{Fi06=qf&lP1$D5}gv8daRMYF1Vu4jHHtAgIe%X+BYvm)*2X#gA9PZN3t-g z%@L4ZlK&`xQ%@j4Kk?D!)&EJ0!8r7e%kpj}Gbu?QD+o6lBuN93V0NQc<}%7&M=u^8 zs9sKH%|RhQ;1b|&PoTMuO2~vW4~G!p`uPJ)eCMgUcK(c$LxG5mlx#nyJPAMy91BGU z&*ogr{MjmKZ*FnJzaN0jpU_C108XmC&Mm>kR}CB9*3*6ZIwl`gqjhrvD%z^m&G4hr z<*x}@kS2;6beP^deC@T9xy7PcN$-agD;P*x=Nc^EUo4+Hm7Q2mF|^mYE~!O^+#jl)--}3C_$3*+ByXx|T~`8{ygvW$>unmW_aRSJ!#yu< z6~igAvhK@P0DIFgua-x~#BQ}^F3#p{qpvMEV59R@4}S5?yq#G;TBv(%4z$pvi8-uO zdxtwMLF=eUO90XWFmk!YoaE3~d{zIDfIZ%-!dFR&<&DQng$|h})u-*PS|su3r?FC^VW6+i+fPiVTueu=mS=^< z&Nu!V@Yz$&<$i2vM;wk5R9`OZ)%+rkvLg`XeOSfj&`FaU*_YaFA3)e@F8{pq>VHVh z|KJN9_{Tcuk!4NPx_SfJ-o->wTSivGr%3D0jC+|qOer5*J2m1i@ zU~pg5Fn*4y2eWYlh>CeW=VTwk^hrpxRYIqDkyKs?fwWiXE96slh1`b`SnMlBQ!(-p zeMMFT#oTmR{RN<;$z6iWN;1P;R$DUEPENrH(W3uGOVn{J!Tz{1WRh zfGaFqM&0?)8MMI?2Q_L%C)VN9)w@4kH7Ukh&ri*9Uxgc}*>U`!{iWw-jEr>Ib$beW zS%6U&TOQ)i9R8rX-Esib(r6jq==u+E@S*LFuy;p>sJn@9oVB;erVzdBW+Pj79|~#i z*W)gTsWpK;h|Yuc+Jjab=;HF2^SqPO7U3u4PN~h&-gTccqm63gclVk*Z+LHW^VkG(#LskQa5bm7?=dX4j z9gu8Welc>dtghFkvfk}Y0aKt&C++Ue_QmM>u5s@!N#0x{`!FkY-EWnNh5;am)cwj% zjRpC;Ac}^2X@GmN$hQ1gQ)b$GC+B9}+*kt|gLI0cG^`)-Qsx;;evFabIL0V4ks>t> z3L`t9+)zJ%5D}I~!a@q%j<0+^L(&5N-^%0e`}rxGnY+#ck%TsX@C3Rh;g)AL=w*SAjXw%Jpu+U4WL+2~r1?rF+(9E*Qdj-xx3 zs1Np0cw#J;7YgS-S;n25|@BRKX`>irV|6QRKYD1)0Us!Nh@C?|kIp zs(&(ElvF#=mtSLX>t)I(rSV;TB0ig7TUGH*6eTrIAm5oby0{Y0-~+x z!D0RE{<~{tTccgOEsWe+>d*XiAZ@nb6$R}GCmj*Im1F*WVmiO()Or2r??t8`$VU3- zh59x}%y7SWG-;od!1i|Z;DI==*{yQ5_rH)K-=8r+1FesA{#N?E8TF3eKn#oX zjZ7s&(7Sv1Z)j(^Ln(`K!jj2Ut)e}4Pb44#QrmZ-^bGAzO$tH*<3BlXW&NT5I_&?f z9_vbHX_*z?WzGAB=smetvR_9Blfcsa!1`CyT(EPdQ+F_mEPw>5}($4kYhy9}w%qgTOSM$P_c~lpdVw6bwacbpZ zO)MowMe&V2$$Z9R4=R*o_z3ROJ^I?bv!l4OlYB;TA*kbg*<~qFeotQq!k47L2HOMI za1!nj9568KlP$#8X!66v_*299w!R-lY`;z*Uuur@`QK~i>SYJ~Hk0uUa8snd~G3fEZL@6350Pz7BzFcV}*D7R#-ROt#MaZuzoH4)A{OGsLvqa{r!Jm zShsHLE8%QqS1HNUBTZ8Q{t80FRnC1 zVk#l~^@X&TrAVP@Cd8S?rA$r^5Tsd_P%GPyZKt%~(3a;}!0d3a)L5oyGTelQ>}n-E zy1WvAc!08j;!?+3Fz_gIJd~~!H(3KJPk#tBJM~C4EfySoY(%fmowpY8Ku(W; zyF|XJeCj*jctd93nbqi4SvA-7M7js6IiXZfk$fLc-j}LH@80;}pDGuv39k~ui(z&k z$#Yu&AiYfJC&T%GHdon$hly8n+bbAU+<*{^PtqfL+nu`KF?;GDTRR^4&-};Nb$4CK zeWGhFz^-?QD<=&b!e@iA&)U^MuTn=v@JLU35&+5U(#J~42m$Vzk4?zH(uIO0ll(mz zT$)$#1x0$jngF6eIyGL7BI+laU!*oGfBw%b08pVKR1Lv74O%!hxr2s=I+VehSr|<6 zQ&xv3sfeLXXp9S8-bh7z4HulSWP4C@&|jBi!_`a@b52lbo4ag1J-NVn{Kcay(^53+ z{k?;xbljSdI5aj7%l;Q^C7A5D?M1bL78qU^HlzYKj%AB{xITbT1CiGR5Uqz2UhRb}SOaV34|1mTeLOwY46>JuU+w`R}z z(VWGV&Py5W5#(8J)lVlAM0iN=6k(Q~5Dc1|u_2NmCJ0HLEfsxLZu+Y@=g0oQO zP7>A)U4wzOh)*GQ1>p~vM$qDv)jJE+kyCt#4v<)WOe z0mi@10-ETNBaHb*aSOcz;A={ki{|b?7se(vn{HEh%Soh%+3M+8?x@i^3_z_(NG$Ir z;AvFMkrw*`X9D>u5j#Yhs&asTvpk8^0Y(l7AS{?$i-9hDiT+(i=%DwXmky4&cxmGA z`XJ6@R`MkqU53uma^d!;?Sllq`mh+)EqP-i{GLJJ#8rWUR$4)$N*)H&sxs0M+HIHb zhVe(H1Q)vd_8E)!U``CZ&%VVh|Bk ze5r25CO_)zZ^tT^W{Hfy?oev&6gY2SnSf@$Ql^$tB>R>*m9soPjZ@hgsL#iKYhNE% zZyVRcdUI8Hx$C^?o54Tj&-Ag-pwJxFn!*AbZ>pmB@I;7zU#{R_eSmQWqJg6#*$IkUPW%D7{vua0LVa-iafM!u< zULfaOOt?1+cXf#9iqi;(%DHu>1m)IaPD}D;&`?Wbx`)1ZuX{5W_t7xruHTzZqf{?@ z@t016Me!4w zmwb7&tM&N$Mpeg$C1YWiEt?%c9?-GA!0}(e`JQe+6f9GC=lNg><(Su&_zvHDyW1Kf z&>uU!xF0unJDf>sX*hXb+?_y@ceoz0T&X3VoXw7IZwr?DSj0<|A?@Be=b>X~(w0>M zi9dA|jxZ7aINCzMLISPzd8rgZ4V$<2vyc6+7!o@NGHgE0ENOvyAD5wD)Yio$j6+6j zM}O;tOnVgITyU@QqDpGy#0h<9!k3?8reyrUBK!ED7{}596w8t+Q@qnsW;v!OG|`So z!T2U<92#cQJ^FJ~=j_FNkYb>pz$B#YslyNjk!va$vc6W8_oXbjMBPGIIXNL_%(5xP z11{>!n1tFv$mYI_c|lWT0m?Dx%_9o+3_^_x|f$$ zmGbV>SfE#GFh9EA-JyGU%vx{e!^Y^Dy(do!1YhBAeIA+O-Rm>mRdUSS^}1;d1xvj~ zE&)`H?pcC`9UMSk7G>yP#6CI-$2ae}F>8Fi3Jy}pb|mS{0~^p0)Z@v)T2Nl{v}FUq z?DCy~IO>!K`U#{u`a~ds8(jw`^2*FXWeTi42St3dS$1`kC8A$b(ZXVa!$-MfL>(hB zXbI=lh?yWF+7fHkxn)jsCOr;n1O~hq?^|SDnQ~QtdT4*+(E6Z@Z*D(409^lvlIz9G`b>YO9sAZba$|0L-u~zoEz5Nzcnm%vfPX6cJ{3Xk z^gKBcsSB^d*ap2cEpwrGx%2#Oo!O7ZGp#fpmoV3ApIj1upw{ey{E?%(#io<0GR+sr zSLzr2730odiunWV*S9&wr0z9BQhY8ZI_vL z`rt@g#p5abB?M*9pFpfdg70qOgp4-L}F+Lpz2 zlkq5G^8Jw^I_obn2%QE%!K5iZ(t+z#alG6Dm&QrXItWfB2t4MoL|Mq=y zOEDHhS+ytU8gt#1xJp}O3$HG^O9IO)KZL=Y}&@SqxUNICov+Wy1RAt#M!D^X=xrOimVO8I^p@t+wT4!&4a zX2`CG0*cfb18Ag&m@DGu?XnTQN zv@&Wv%G(eqcR&5dU!);Dm1yT&O!Z21i*GNdQ%555LJE3{$kNc_lr@qG$tL&MaNpE2 ze?*MkcRi7@mnJnSN5v2i@0mqsSFflM?N&10*C6mdOZd-T6ZE~mdAxoWe$+gB?AnwX zO}AMUZG$nsvLSB6x$n?B@V^UiVCp#AZ`G(m9$O;XiMc(w=-U(+UC&i1Xo=i$2W@b@ z@OTw0hZ@8!*R@I3h3zKLk1x7D{wjb0r_^Eg*fjIK%2?85%AuX`^ile_3*RxGyAlPB zrvu)r&;tKppXTiateD!^X7`607^3?KDmr_`30YI~pT`CdVF)FyK6jyD&?&3fwns!? z5p?V&*BX64zlh+k-d<1ssS*?q6pT^AQWaqsO$NjQf60?edb%Pp3``p3GPYO0Ey!H2 zlqL~5E=B9BT_b9QczSwWClkUqRo^g(w}bmWKNI%CwJT4)S$Sl=#=pj&Qmv7#**B}X zz)axFWdD;z4BulpqK+T-mw}T^aHr75m#JWOi8QOR>}tAe5E^5tkfnl6<7Ap>9BHqe zP5gra5$CLITuP=xrxhafGORV&W4XXntEO-8TE%(c_{@8NitPh*h?z@eGr3#ThayfT z>@hJ}TsWiA_y_~o^+YdIc0&%G5mR^fYRCvR^NW0Kf2|%-o4~gvYveJ8 z&0Uh~mQulmVCZ=bzp6+X&*N9JxxQ--09QsieG(3Vt}I;~uc#(ZCw(|En4`&VPGRg* zMnfZGYB~LEp&p8;seGz@$Ty~NayWn5qJ=GK$fQ(i;aF>+?ZFfKIiV7LO(JVXpdT_6 z{D%0)@WY;yPMrt2%Z+#w{uSICe=+ktcjYF`sngfG+(QmIJ?LUQvPyDe{WNa_*L8|E zTBY7y(Tv2se1FU_mC3Qhh+;}r-q7D6l}P5?;buq8*~CJw@JMLwFhuuFf0YCif4k4- z!g=;I{A28`33a<8F-KqBMOcYwcbD-QHl^`+b16Z{GS}c=A7d`DbiXk9v}y?#e}?ez z_TCw#IeYZK+>N+E)A6@~{Xp8RmD8L zi;BV0MN`#D%0&V->mH`U*G;UJX=#iVR5@>(hKSEia)% zK=*HB)w)$9{Jb>kI7-`NTd3TPPOjwn*yKcDi6*@P^ryLQErb|3ke#>#@dC zK}?igpevD+cffCX3`jCO8$GZPK z5A5}CfEAR?f5^Wjh#kyqeeSLtw_Q>G&40{O|K5%`XSmh`E}z?{dRijruA@Qm|F3E$ zb+Wi#xIEKxPswm!{1bI+f0NB*kN(0=KzeLP1rJx_>yd6gER?hS< zZceq(c!%lRXy`Fduvmjg5`8^@9#Ti9qQZQz=RbyNG8hPg@Gviu*G{jn%6+ zl^*ETG?Au`j}R_gwuol~n*DBAmDr!o+G-Tjdb8DH1#+fT6j4D*dg)MddD^&x&QeeS z8+$>q6+T6#<;E}-Oa{RhMqao|tUdASr~)!}UNV!3n+t)D!_h#~48lC{GqXUN=sJpL zUX;tcOu%Kv$GE5Y0Rn%!(|}aI5fU2pd|BSsQIqiKbC0pAngkJ5GAEHGEDPs;52EE{ zZd!3Al8L0l0u;7_yf#5)=1w}CTZ$8N`NIJ-pz~UEmY+B!wFPAAg(3XD{kCFf>ivyt z{ebr4(DU7Z|2=r~!*J~?@M`ll4lu{7S(i*1Rl2k99pUjdkBZ$>sk(kdspaEXY$*J= zxqjvNUiRT{n;TculQ2K)QLXoX-RAQhiSlElYu>R}$t zn+)k;E5P2Vcdjl%-Z=pMdvrx;Xt&hj(ibNdk5M&Zk{3caeLZ zU%-?;*cUS|U%UMq*rFRplc#D+vys~2**tTc9#U@klmFoFR8V~zyTTFWB&un+s$A%& zji94NWBEiv>@ZbTSqh#VQSh`?EXxRjLJ)<1{o1KJQlXuOrBrd$w9KhjUjy*xy9={ z&ia~iy!pCBJlSZYce`E~lM3#2hri5o$9kXi(YcUr!@KWiWr>!yC)-J)V{T`=8#z@s z1Oxa(TZDJGbYK_Le#StmIQQeB27@(*AqLu{#4T()=javx_sqoju4RT|P_+Hryik{(h1oG=7*%Z~3v5 z|J{M)8G4XuOF-5_c#cQ@}wZ+p+v?f!5XH?zS8 zxSNMC)mu!~uc3eVv0I3JEw#RG9ePp3Q+P9n)Vt z8S5uMtWzZtA-Ogp!W>kH40IcXL!vy?26&B{q;~Uh-!~(M;_44I{N?_V@Y`e+b4|Fi zH{GwHQRYdFG>i=(LuP@I6Mwmv52eWKZd0EIpkA_w$0X8qB~7JHY;!B1JE1m~Cnm$0 z0-9FdXXGW8H7wst#2QlvH8y{0n2jv_ZkQkW(LTk7hW4jvjSOd{-O0?r{?fliGJ?U> zrM9qt#@9H$!_Kn#9Ty9X)r$X?u915n2K%(9FdqeEnD6C!N>aTPt5j){IfeT@q)T*{ zf+}YIs^RQKeFcke2NrjdE0#ABsmp0K#+>YQ<3D zB(ZvGI@*WmzLN(VHiW-|GOXkpO_~Dl7G0Isf-rud5${zxCy}qtPRThr2OR-mV~$P4uI!X92bvn z3o)toor9~Ad)rS)<2aUDg9ym6coQ`c&-bH_d#;XKo;lTc+?3_tC0Csz^Tm-DE`yf~ zkfbzVC=1SvMR6%Yst(a&0;A8^$2?m^W2DTLEoCp?QFsElT?N|_ixCJsDO0@ug}W2F zd94G@eCyM8w20bG06s;=b;X_QqHJ@rf12zZnW}tn%6TrTNC-okV6c9G3PN_@5|YAk zyV<&OTYJ6lqFVt@kGy>*qd4rWH6b6siW+yx430W1PK^$V+*K#97KyYFx)62AETeVO zUnZ=95ec?P&)UN}@OPRqc2ed!I&bg2c;BAfIh&>aW5eqQ?;JXpCz!HNnW+)@4lDfD zEdLvq8XajASy5?HU~=1yXYH7Jjk@jqR;Tu!hiGe7h}Zu$rpmE(VSEgo&i6qL81;8w zV8c&p0tS3mYmR=|^H96%|9K%N(Ew^i@Gkjv2c4J#u$!`$9$<;uYje}V znsi52BEs2BxzyEgbyxma2O=x6Lz?`v%a~is7#8OF+{(AqHbcjj*VWpLy~b4rDer6a zLAHv%55QdkPS9HMv}fMVaQ@z$IxL}bP+xf5z#uv@Q`I{`wB@GsXc$~fe650)_HG9> zTEb?GA+Sze$~>qR7+w0!WtIUMu`veOCrmwb>nxqoy^7jPQ%Txf@CPK|B8_J zl%kat9~72I0#4)g_O(^kG1)+0HR@RZwfkwJ8?3byJx3^7I!FZ++E8|OKSxJ=CF)aI z@F%Th2n5-jxW+K$(<#Y~BNmB@L>{vr?CHkRuIE(1W)EoB3JD4Y2TE=i_Yx+Ec?tDi zISK~XFsZd3^AVCOUzw3^5VCeuW(|VozW}JMx_KXqA5TMj9@M2j4hQ4%0(KCgMFwuk zXqzzf2v9ShTWE{fJ6p>w!V*H*v)Vw84rfbS{aJ`GfJU>W8ZbdGbhFOy?>|<}fQ= zfUW7#UJlXOzLPF??Avx%&q@Ekw;TwGC8<1e*a2*iidQP48UYGWWVOJsRv8&SF5bJc z$ni-0<1?90N2?w7151?}JE2`tE>%n&!6JR=p?33S*NBzrs+mJ_l`#A()Z^HA^4H~~ zqj~E4G0V0NV`elo)jN;AED>U*vT2R=X#uA5{#lEatLN3bqy5Jur4P`{X0`;jI-kf_ zS&eERS;dh03AHt~LNkFIvkJwjNv_CCn-O5c3ZjY6wVQtrt?K9Ax#4y7^GTEMjyYQf z`3>jPhiSU)`X1O@?i+J_A1voCmW#Lk2aFFDXx@fO5?*I>&osI(|D7WKx!ykn@GoyH z_&8=Q!GZNQ8>vp@f$Yb0tu=(gXLyb;Y=?V;hsh@~#)qwcM~Ky5OfNJn*(JHq-q^eT zXs%j-A;CSUc-aGw#GqBSrC>?)rxbbkLu(Und+X)tIfZKESw}TFDmhTeVSNHvrGq!| zc(D@Flre)ju?-CsP4?F&g0~NecUtt5G_EgXDOyVAjzSQT#=g!Y+7+rLEK2fR0 zmXAro)YsP)V;uId`iLv@XNdIFm1aD?8V9@Q&7Rv|zGK>C-i)Fe0HtJb7jJFu>Gru2 zTwX~MLeeL66O8qh&5s^nnpwJ9i#58>(IC>rM+4}IYvi})lIv1V+lM}J>vB^g)uc*G8%+_pkU3B8v!W6#BDkPEZCI~MIkoRtfiMk10-^C zb)A;UkRg-BU;*iib4Cmo&VC)gDZ4N{t2@xYiR_r-sJr&R&Qh~fF>e<<72Pn0 z^VFMbiDAEeTc=tyCKdfGI49iA8~YSrkLoj>v#(z1V?`MH zMf>oycXGCG{v~0k_xqsLFI7A^Ly~~ z32^qLo3_Q%vnG0-#M8I6!@!5bX5hIzukHLt-3_Mq=flPI@KG9JZq|7L1# z-R}7vV<4pC&9(ir430ekbi^m)xppe{yfO^6NhIkn=aNosd9kA)QLODsJI};@@tf8s! zWF~mulm^-J(oE#>DBKEeveBv&x9Q*`PKW~HhtLv?pU7zamGd``X|z+Fu8aT9N2wqR zqMGirbz~JFX0S?8TU7k1piT?NZ*z)u-_M&gI92k$8^=^SO3-T(AvvG|pqegyvdt^5 z96w1sANHdV&aOW9`1=qW3a3DEwCc^(4Fp!LeFP1x-@}Nyc6-^ZFsc?`m`no?t~kgY zmql09vmDt8ynS(8J;M4Oe%^U`_~$ULQAQx;s=|t-OKKZ%^HkB3#1i#yKMq(*QX*nl=nvs zTP8sXKs(S1)xwm(FqxYLeZd#IWh_2P*>te}^rx4-W%9h7!gRKi??&EyUB3HY>OLzx zy&Wznu-EI3ieCdqE|$rz%Pn;JNAMSVg6aPd^tVHgwa?zP+Uj;@TOv253gDYppnZo% z*jLbneUKro=Gs>=Flg29#x_RuX;1$j7vQNoPigckDWy1{rH$ek z>KLI7?@eAt7Xeg?GF9G8(!mu@!Y(6XV*-k3AsJbTRi()oxG&%%7+2Uf+m(`+QOTb9 ztph#0&~*gueY`DTOf2(XQV6V+;t)#sx1B#`%D;W@-m(YWbQJeJ^x$YP;*@HCY;O@& zdUJ&rj>bd@q0soo_akv{5Sr8fu)ZT_Qk94z@@@Y8GWxO4+&A-vO;|Zs8rf7%u6Kq? zPb0QUl?|L;<1f_c>F~I(xwHLL6)M2XrmT_D(9O4w6?ghy=JFZ!}Pcv~uQXycnf$C(GjJ&DUf*MjLPr5)ibh`|5~L&#WV;6X-RRFU4i! z0yQncbSg7q*1D6nh;uzNCq$*o{g*;cN?YD02IOTDV64wU^Nsn(j%7%a=~0of3S;s! zBfY0kEY^XQ&S1`)@qDMAIwj2;Y`(pTPjY%t(z3KwM$42P9X9Zz=l>KM_>+vsul{n} zV4;Sv3@&?()Qxp+S>rZRZ(h}>|CwB-R7qD2W4_I9WwLz`rzL-}#sQ#gZ1y9PpU~>o zig+qzIG^~BiTTN5PW)*s>GH^zcSQXs?tm_zCy)cYF6L^FLf9sU!h``-445l~5Bl)b zj-hZq?$^@yiKBDQd-&ow)qRjc`o=`MvUP8J`%HPv#LiU_6p6WmVZ|MJrUhx_8D^ zjz~@2w(9~*pPhbW$W60ZBjqb!t4eDw8B*3oF2ogZRlfwFF!>ow#+{1Ajc@>Bv`I!G zAZ_k*HzH8(WoNf>ux=_T=F)^9B}ZG5K8L@PsVWZv7~>xMT#G7e9VjJ>rJHM1y2%%w zp+cXHrF2g1A5Qv-8C6IT?!Pm-S*hfnVoH246`LYeTP)OR7ffU&6EpSkT!Wf0nv>Oa z#gUfO8qH{8W7#gma+@xMz0VE2dyjQ!;CeEXUH*Q-9{g7}Q?n1dSG4mP;P4Yra^sLT zFS4G>g@uXD8LX_X{&yOUvdBo5h+Z~@jwk0I5J)Sbo0h&)M53ehiZ=2!E2bc4s9Y*Q zuB5G*d8n5R+CR|7fkeSzOgOCd!2QL;oon62frH4uceQPv-v|o=qd6`G%fUwgm$}xw zJLZeGUkjRO?u||8JuN$4u?Vz|kp_ERLf14#1kKypB=20zJq3d;ogP8<0858T$kX$; z$BTk9IU}nbv42QkkJ;de9=`8e#aN^U{0@-)JKxE0a@0%+fq~}SrS@)~4V1yf5cq}n z6hk&M1M5XyHwRNrF)^;EvNpmn|mT&((i zsJ?J=nA%N&^kcKur_Ot3s*;g=4D@@EZut&SdBE3fWmPr-uif~&5P4kE9w8~)R#ZIi zU(+N#y_Apy*OSCI{BW;@Kd-K#&StoDIApVh*-_4ParEWYwVd6Skb`T!5bj{%pv*dR zpqSjkBT_CpVK?dRtLO40B@$GoIfxoMqH^j5>POixD#jn}Rdk``b4Cb^ov^S#Z?#u1 zUs+n-`dpI$3JJL=Upgvlo&gFLt9Ub7W)wN%W!RwPIrxD^h>3t_D@!cZU5YuE8=py( zpn~eGf3r5M9o@W+A4>sRs=r}nEB?L&88jsj?h+0ZCq-07$|~V%1D_a<4f{rOZjf;- zBwj5*4}Q`mAbs#|J5l%E0C0d&YR5ulD(&g?ZL=5i*@^zbLD;b@T5?2`knwX-(AqhK z^2(Ts+;b^$$f+@5v&k^rHo9Z$Ltlw~^d9Hm)njkHA@p7f83oHMc)`m@uj{Zw?C@)+ z#g`sZ>d7g_RdMAJwv&rs4XZ{b2tQ1bsKh-ySdD`}D?TtI-w~MbQFAzd0CCU{6a83K zda+zq$~(`C@KUZ1sY-GKw3oeBH+xr6!iJ4ido!Bp`bUld2Z`0w`Z0dM3rz||VX0zJ z0V8MT$)JDai+~We9A;_N$!xqlWK&H=Ltt5*nVt`(b*~#;U8@q|h57tul=B)7Owx&r z5LN@{Gc%V6%hWP|RYrcBRQRjpkf>f$fB+B|7pLs%wg{Z|Rd_TkPM)z_V5!b!YI?it zWc4yEj>9v5$_#z*y5U~sNt&8&OPWG>KsjX`Rsj!!ZQ8t+Wy(A3G!6HA4gPa4z!Yw! zy2b)ccMZ_Q2t%`i#3JL*9!A|YZd@+rxQ5=im=Eu!+hY|1f`Do-Pe(W)qsu~#T1+CuK| z(#$20+nM#MPAHrR9hczizW-3ks9VSo6B8l(N%$4j%cHP(?yIz-F?mZR%8OMtW~EWM z<}2`z=CWy|LoWj>zfj~~7qw zYS0K}kEHceX!goh1lsC)N5=;*=qHiNvO<3}>R7@sQJ#l~ zFrXCYt9HbN?3!!^^vb)J@(b;_XZ_$=cdH zhM_xAl*7e3SdJ$qH!1RL5^)n>Lb1!$w%~)DWq98=Kz(oEo4)rgsf-2Qp>{-r)!EC* zIlFz}Ru<}?eRx)LE3Mm9Boh&3xKlSBT9yN>0sQYgZ3gbW|0Te9d-k(cT5#bU){BKIN-5Cy(BX9uMO2Lva+b9H#@1SA49A}a zC8HprB<wBuYbf24DSVElr|#*S1cX65c)u?R;BRV_|n`s5|5czQ zU#Jazvm?hw2&G=)zuTrMeJI72cy2&o9C*ePfu1?#bS zE5F#%gvTUO=0sw;XzNUwo3m|G%0y=kdN_TsH#;3@Z!HqUiHaQjP6yXpOb?$+&P&qh z|3`qAsM8@Pbe!Ssc8{@48*!WXj?BYm=V`dvxZPs=?Q0mX&JT|7A29+dTs`LpZ}C)% zpwED-`#n&vd{F%0wwrGgp`)r~{uFkIOsTf3xGRFm{*4fnH0usP*Zfv5rIMGZ$@d4g zPQGX(o2wv|oCZGSu%F%#ttWTevAWg1lIOI~=-OntT42%b*|qAKh6+;`tA%>zPj*QJ zx=7B{Eqj2wlsw`j)oY`oYvz7+QS0tlJc`9P@)uXQ?7mJn`*W^vi@c`w000SUe$6s- znUk+F$ZCzcOP*X~yM}Ms%h8*i`LQ0IQ`joE=Van^!z1o)EvGjv7dpv%u;Tx+B4{0> z3>F>j#Rhw)(E{xBE!_-8?<*wJfqTq327oRN^sMtr?zTor%br6V%73pJ>?v~)BGqRf z!9;L9=dO9+xW67p!EA>|%pO^|K$`m>CSg6)XZbqO-TaW$N`rOA&*cZ?@!WW+A zxU|!b{*+UibNBY6P6JYZech`Psq5lI`Rv(yl~*>9W_IwNc+|qXC0NH}J6{E!2711j z*Fenv`php@m*%k>g-vTbaUN?uZrrG4LipLP>iC3uG*EXo%*ua+3_Uj(w)z?IGTYI)h!aNI_x(c~`|)=c(f6+IBNA&%kkFDRoVnaFV%l`hl$53TG{Ke{*d{Qs>F0G?(=Jhw%grm~m4Vl1Ebg?k#Kn zdQlDI5K`VHS^SR)1Km<@8Tb5|DI0Ys`?j z#O@EfJ)5)Jg`2)F5Tfcq1XKHk55H$z=*5q^m%84YTOVobBQ&XhQ)=m@{Ydsh;AEgP z(?Rn;-WKuQ(^4D?*325fbVjPLS;*uW&xXHt+I%CSV%dCS$Z2F-Evtmp493DnV|u|) z@?_d#Jle?^)`$E|RV>-aT#b3sRZ{*lj$yNNX-Y0$O6hWFjgX8{JRrueAa!#rS9-`1 z?$D;+_4{V`BVx`z<3QD|ml>=ag{;MSGsQ+17}!-s1cws^-zD_V0Y&p1O36HT)J!d% zP`LE(BC#1MT)my42BxRd*nog}HK`{v-g2CZP1EzYR%DPIx9)es2l>=-abc7EpgfW% z(wqH01HoVZa`AJq#-B`(@?Mu}dr6ozabHv|&H1-;B;SXX5`W zIlcX_&t#ds9u16le_YPmH2lR=2y1ng!PhD<^l;DKFAQJH=#gcsF`Ah!p(o1#5|DD1 zF8goX;?gtHtWS=tMNZjUS^aspCA2?taqh1g?`EVwy;DLbqjuI=>XO~m5mXZY#^8*eFBUPfqAUR9_n3)F zvi2)jQ%tPS=XRX?AtG*^zd^#f8T#CjpdB^$g%wh)yDQM7n)zP+_9A(ctJGJC(_6?D zhtXe=?8A0gT-i}$t3y|1z?o33vL%StbN%Q0getETHthpXckENodl+vdwX@W>Pu)-RKay{H2g_$adDho+{r?A0wO;zH;J zkR3$d*SR#Ppp(iOQeSJ`mxtD74gMY3zAA9kIQ08wyBHs;eU1WO20K1t47B}?b2i4z zCFiansdPeSn6~wAumJ{NR+1gI^|4FAJUxdZb{!J&N8sJ`yZ0)gpI0dNEa?QLL8?uX zDE&XRuPftRbMZfr6GpyiDUbEE7D(=(l24H4rpitbo<^B}pKf8t&aF75nc93l+XNC% z0ZJ>sn7fx+RaM+pw-(9%=5c(gXD_fL(#NmMQv19-9$Vk*KmT93o8b1x2Y6|E=+fD0 zG3K^1=Ze_md9g_I1xE8%%Kc-i0XC+S9P@?#F2qgA`3Pzx%Wi}Di?qRCZDYTZoZDr4 zI(qp=<|b7yWbuX*1z~hse{Kr6su#z!)&;zCo4vSzW7;d4>72Cf=N0Hz2IAe%o3H4RPTQ0vbRe2Q7{N-m$}wG^4iT>HAtfO;O^??IT6#qn zk53G?pv{S;Qpc!tBbDgN+&nnf+WrTcEbPemOmt~S(Pc-zRLxkY|7s_LOR97+AAIJj zw#bIv*%WT>D7 z94&DYlXH;Cv9-K=h9(veb_jIXUc{G|hw)q>l}AJoU*(zCS(YRj0qx z#zErxoJ4NTk;MQj#*9R|vmAxHypQSe&ZQXZCSZ3+p*8aA z#gaL3lER4;^NL34F~F&{zDW{n7hT4ouCc_bBWksXUiD!uTxbtsmV#AH;sL_jv3=3d z|8HUlQ-iOE&b8Bc)u5;x77D|iwA9J~D_oG5MZot86+NcfjM+8aACI;lSnlJIhi*qo z&#mKK%$ofvmmz`tF1n*DhT9EZjZF^rDw$z$US3VwB7|o{RkYF|)jf0ZC(1ATb25ZO zEHj22{0kIr5xnJH2l9=xGQ!p*eNvTIR++MIlMF9tsWLX_f9d2Qf<^qX)<%EZm#yoP z6h+In6j76w4zKF7@b(Xs%I2Sw@_9gO+aB0^S9k|GR88&A991PS;mJ6s>_gn~VydZ> zwX95Xw*n|4Bk0pKyM=7iHv$*?Pofp~ktG!-JC+c&1+5-C42gC+gN*90J=FqOPqpLR zLXyIZ-^5ZQGVQQwkj?7F7e4c~(}Wwk6IA}t-}IUxZ5uBI{FJQPT6ghi#j-Z%hCPXb zPiP&R^!8{omua>yx5KMu!U0l-ZA>1jEY++>#6?8{zSJP+?-5i!rox@}?IW4Hs#sri z{4X~7pNEAc1)vgKEvuXUpkOw(V%v$87vl|Psq@}j}A%Y1iCIY*cTo_#WmQYJ`6 z!Sq3eOg9E{WKwD9t<_~+lyV(rP~zCm$gTIZS2AHPgmOH%$GcFu2SeDKbdgl;JKqAO>?Wu0sb! zi*$nNso2f-cglX5m&bcGBeG#e%AHOI9ktQwRtt$2MRU%$vB0}DFM%2z`ynit2#c%3 zLbv`&0<9Q2`_($X0y?z7_jhjb&>sb6;8jq`Q~WT9HQ3{n7;JI^k}#y>-krC=;LnK9#Og}wIf82h#{#yTv2(O1`oV2=++!o_O(oZ< z@J$|F=Mw(9)zfYGD??pWTN#>$#hcyt!5* z)Ol=1#Y$gcZm3+8qP|)%*p(>y7;DCml@JA;`DAS>g@K;eHi}eU4We5w8|lnjGfy~_ zu@M(;4p#Vv!Xod(mpf3*%Ox>{!3z9QMCXdZE#qau2<;<#B4@s&QRepxaF@Jm=6u7u3a(_M+o{JDZhl{4%D#jV%lWVT(;5HcV~(8-N~x50*amktLlB&HN!JhsP)wg{ z^pD)N4gWvyP;~^5zt~ynu{e68N40<5{EOB)(h(f-X-Pi7Uzt-5gSzDMY6_lc<+$lw zMe8{?Q#GNbbA%GzRB%WrErZhl<*jxykMe5@A#-=OhKgbnlC=}N<|3Y&d}|&@tJA1O`slt!k8kbCa|tU=q@F_k>X+Q@T8S(dXVYxGXESGmK7)HA06oSc9}l)nh=kv|>tl2^yC$k? z^EcI32Yil&PL-vHVV|;RaCob4vtYHMXY`9wP51av3#DC`Mw&@m-AR_E{~jp8j%}B; zHIb010i6w=HO+EoGdwCU;Q6Qzq7FCx@j(4?5l#}YqC1kh(nVETNpM*|-(xcRZLdr7 zRDrpwSPBjT0R#%XP9m{nx`*tLk3WvDZ=0{c4+td@{E|6ya<0qtzvi?55H8K2 z`g!rQO>m_uP7I~su({W))(Aqzl^yLYxher^Y#2|&WezxyKJesAmg|6vj^|5GCB>C@u1T~} z)Zu-$6L0y*Vi2BNp_$q;0P6p70cwmJcfXOdj^I(TK2+<8YtU6fX}y*gj(w5XNLS^! ze0%8aAXbylgMFT5V9$JtVf{ru&q0*LDCM_v)G0Zb!Wa-jn!xdRN_t>Toh>e1pbaDU zoZ2&GcCOmvP?)98*u4Wk+a#@Fdt^AjK(#UVG#Rz? z0N8N!6U$G0MWq#e-fX~4tNtx-p9S;$CP)=1!0fDQryII=ZvlE!T+3o4(edAn`kJ1^`uom z#bT}Z6$2h2V%NpyB_j=Jwcc%S8h;+&wD+kwTRO8V5*8Kv!}1WEN=x{Xk=VlB$qn$> zRz*rxG;I|}aA{NPR~p6p1%*P^P9P!h?eJVE6oYE_b{xafD8DqB^c;Tdap|kQgv$LK zBQ@oQwO$fD8HOu|Tdp&7Q0O-YCnsK_iV$WIN0M0 zv*j5?+~zQhv`b8Ike3-!{S*Rp(smLL8Yk6sBQYW>%T*CYM1`5WM7|Ysek|P_pjRBz z>*n?qy*)qf#Ce085r z?ClA9&o!bpgzHvo9qU5b&`c8|mZ{Xl(w~%x__D|_076a4VWG4u8tk^ee8+N=08PZu z(U!I_;n9cJCW1CLHb1G$ZbidRva*uRq-hW2Wyuw=e-sppE|4}4Zr88J+;Fg~6hK*I zNqq$v!NT!V;lUO%B1w{Qo!bh@C&>rdm|*mumeZ7&as0#>OGNoQZ)Oov^3wwT=bsSR z^!TNGuCgf!J2Y7j$xO2d<8a7qSaPYH7(3=U|%}EQZOT zyv175^D1o|&g51DJ{~^i`yOKqKeponq@D1-&_aQ|4{{)CS<`Pv!f$1P+qeP7eVZS! zWe;}Hw@y5MC%T)@X&={y?`w5$&j)#dxpSn2H`##?5P{gd_xX<?VWF4I77sA9XW)R;?bEYf02fk#01}#&}SEmw#S1U-u0^QVgGNgta>^;noBC zK1vLq|L>I*c;onf6ZjtZu@m^(7x;W+xkGa!5O_T{+IIzMxz+qQ{&+p_x_B!t>-f)Z z!rS+2Q{e~CZVyi4mBc2(=ii_9oNKZD)3%4Vm27gV+cOC*g{@*WW#Vv-nWlL?lc40w z^r}*utxj;%)Ws~rCVab&(hsUxU?@pqQD0D;gwaYpvMo(=9r6x25{~XIaEzq?ZSST> zVc|;XLiLM>V780nmD{isWgvxnb14&V4E7*H-w327NAI2LNprFX>ojO;AlpxNuh4$C zld9RjqQ!(E`InoDQKA0N6osNC16NGFE<;*E9-Bo~9v6<>*yu}%0{3Te<<}E#2l%0` zg@Ci=cW-8*_t3se`oM#W*PL*4lK0|+yE%Kv`r7Mz6@FV4ei8m3(DuDS;*w()6bZ>! z+`g-mz`eS6X5ph%$Xe1}UsG!DQ8}uAh>W)7SP59^V4RU&MeQ1N!<*kiY)DX0W8_8|3nqwlD2$yy)a~W8O$H`qQLR_96N)WHHNIO{hBMcG zClow*wSeV^`ClB?`>NQRkytETW1!f)4t!^aU?M9{rYqfaKOL$4(@yLq)?N+ z5qN9qyI+34-+W&$LpRjd_rD&a&hvX-T}-N?)>LwEbSU%YBQD#yT1nX%;!t?@aaR1v zjQMRewKZqgp>6$a#2Am6nVEPa(CPm$^^Sp+hRf1uG_kc}+n8u#+qP}nn%K5&+sVYX zJ+ZBuz0W;!zW?jjyZY&_>h7xk>ia<0^8Nb0>a2y!$LsZcef9P6^+2ur@;B!_!PkH< z`|hdxsq5=Z^=lRXUYNVW6r?`#1ZTg|c=wr|%;2X^;s^X{4OddBc_&I_~e zk+cg0kQk+~Urk%-Jg=g{iVy|dw}&x6{RSf?ct+JnCHih5EVm+7wCAX#Uh?dKS*&ss z7ZNWzfn;hPmUypvZggb27HAKKFo(SkI#$oOTp zH$Z)A2q70;m65Nn@|yW}{e%E-?%QxABdi(sOr_^VXK*d3~M5 z8CAnH*{mWPY`k9sr;FXq>XkL1>iYJeLGDj`NSx=^k3a2Vvj);k3%yHI{0)9jEF&Z& z`zz&He$7eJo60+^2H9>A9drP2sys{Rj_tf1S4cN-O8k?fWBA2K(uQ1?oke-yWCVy# zd0ABA-S&_V+^|3@?PYkk=B{vhP6eU${un5r#Or-8t@D&5o=73lvB#PEYS~8PB zC6mwZHiZ1t+&{F0XILe30|i7Su?Lw zqF2pDQ10=1T3hY%G-NP+&Lr?t3{(ZI@N4(${V z%l7M(E6uX^nPf!GNKDUvvCZc9vyJ(j{dxrSd^*dH1@=5WeO~swE`OcwM-*J6C z_Iy40zQ=ff$n3*d>$XJouzFcPuU$XXKSTQ$^1iqSCzC7=O!F)kcM*{-%Qt1T`UI;T}N8Urt}1+@4oJHE6!m8 zSrwa(SS@HFE%OzQXCIwO@d=Abgvp@8Hu5q;4Nu9BnTeE5t@!_5nJ(cu)DG=xu#gjF&bIQ=e` z#4>ewt-XEWZ*Pr|h+V7i`yg0x1IJ4pja^m&pWabZZrGm;MY#ig5)-}b1`yGKuAEn~ zVaAMIw?ua$J|ud|l|E}86X|-~G1;BXA~(=plHaF{Ct05J


UpRpvyipUmZ=R&sg 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�H7(nOCl!E>9l4Z@! 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 L<$yK920#7}i~H$) literal 0 HcmV?d00001 diff --git a/backup/img/login6.png b/backup/img/login6.png new file mode 100644 index 0000000000000000000000000000000000000000..956d0bb901258ecffe5bbaff8753810b37632071 GIT binary patch literal 78484 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 literal 0 HcmV?d00001 diff --git a/backup/img/login7.png b/backup/img/login7.png new file mode 100644 index 0000000000000000000000000000000000000000..15fc70e418ea0a54f280590d53a5467c21e9a6cd GIT binary patch literal 103162 zcmX_n1yEeU()C4x6CjY_wz#|765J&O*Wm7Miw1XhCj2rF*l@+DXQ3y}~06>?K7FPuT*kb^Ic}9YL{iW2cH0||;>>#b>3;=KY z{yi|s3~vbmfDDik7gh7fI%>B}A|7<5_OKT>>8Sjm_FnljEe;B*Xxv|`lOm~Brs0Mi z>A5dD?<(zDg)Ym@_k}laI{ADbLo*V>&rIMeNK!Eum z5gG;n!w+3W@`?Z^3>jI-IZ*fl7r>VRNLRq^8U3|Kz_C(I6bkl8RSun$mM`^20*1y! z8TzdS@QDBiBJ*)NJc~G?7HL+O=wtFS5-Cgo5DzA+4-7~|1Yo;)$;ilnG^)?q465RN zGbHO_bfNO!YxzO!CV}UHm2ltB$Q{+9%~TsxkuCICPA%T!{aqd2ub4zy|I$z(W4oD0 zmN{~`_d%tIPojq0@fSJ>1XOYXKZ`nNot3V{L1~ya?M-|3p|&KE-?v#JR0-ma`Wtv? zEn&r*_U8y^Ibk6BRWFa##w8+HLyS2we^8_-08CN(cd)QXq%@6d76cbZPPYZ^L?0>E zOf0b~O>E}HYy7J`IiSzgIR}|;a#Vhc2jeM%H5l-N73)xFO&VilVY+meeB9djq&i(8c z=P#6Ci|wO%)O@$m>ojU0?&+-GuR#$B=tBJ)4D9*yT=eC5>LHh4!8ka{6D``Www{F<;q-fXR|$e$I^K9(L}=n;t#b=cRe*-4LRyz)4;`IuHR-RWLlk>l zMRuzg=Ued+!J))aZd>8$nd0nk0c5wGnNoy4Y-H@4#?&wl=K3#T;v2UR2=Hy0oDK$f zXx~GSz>(LShJi>2&4JOdV8V}-ILs@SPnEcJLX%iS(eSDB!o!eYkiP+5?dVH|6kv}>uM)hr8V zu`V)^qMa1W24YNkbm zc0zrtw%v1G&=3KgT%v+N8~NENm5iH7X*VGnkEvfu-_c|0eNBM1wV||aitUQG09?bd zZ6SM3YqZy!3pvA1Y6&02?EqY61I_V+6w04rsX#Ddhwk6eT>@j*`=w$6OfsthFJOaY zZ<{qaDihhss@=nn0N|ZHp+OAptipJZ{HN-o^{Hn*A3Dq*WlA{*BuuO@H!ttF7IUcDK7G9h- z85F3gAd%CGc_(2ORYLt&?lZoVF8`oY^q{u%Pk^42F2{p z<`eg*U-If;fu|mZ=g|`q0x41Y{-_#<>vxs<2ivxQ;YS_*|m1s3km?yNhR>fk^n2Cb}R0Gjasd%0hX)9@IBmZXO*U1;0JWt!~ z1*NXhJnW=0GdKC9s_$3>&jG?0-G!?>&uNIBo@MR`bP%lElJdjMtdX(`x zv#Fj_pN$|DXNF!aqp>CN&)*DQs@An%2Nw$QL>FYEv-WDr~^1g#Fi z1b|PhckHro7aL;_wh$~V&^f$;QL!$r8`z@;!!v!VMVpokjLLrs#pYo!a${$2EH7Oe zV+x_60f)gOh0FlmA0q&52GEx%DqU6+2w1#bu=nl|PwzAQ7SRg2+3Dw({q@EYcyV9= z53^V>e-Fv|1CX~8%K&1lP13*{!ecih(NlJ4RZc&8SV4s$NGj>W0w)mMS%JU>Z06I( zviPu?iO4b$5TvlpF8wd3zpB09h$L`^?!6>ga^s=9abLgcZ4qU&1)gRon@{v}>hbkb zY4zrKnqrWi7j4?h5qvjlCSsjIPc{GmJsdz;sZfZwhT-}$uZ8yd+#Ur)4=^UjYX|C? zA8iGB6-G}L;Q?Y~;4(T)S2pfCf2C_5?}Cj^TxkGV4l~^oiE^fS4Cz}r0+R)xOqhFe zH4gyNa4ogafH4h zRP*=MswzwG-N=xj$g>kD=94Wt zlpqYw`XD%{orBNG-u|~FB~W6-ksA^05eW}aXL-&LuZW0?6IfK}$J#M_i*6TlSKLN~ z&{bQ%TU>{)FulGfa8ZtYTl;H~g9+(946N9W{adjBuKacyZYs7^F;drRuE_=Ag^53% z2m#=qW!MX6@za7Gu;c~vGS~nd{1&nj$M3Z-MDDlxBp#r%h#wotU1#+KKP5ooX5kxB zZK7r+<-rRgJK|fUaN|N9&DPWo9krz$xR$fhKcl_);M3qW-%J5M?WDDHM)b(v$m?Lh z0662L3f{KU{_-R!xe7msLZb+H$F4)Cl^Hmr?LbmN6MsBlQ}8@!DsR%u z$>PY_?T-z^{4%Eqd7~TDA_|@eDt2)o2q1~(I-Fr%WBf!+G5KJNqK^Vr_(%jG0)^tv z=&UbPB^En=${9oH3!ok|CKA!ieO<}AIsT4ae0IYKHSv*Qosh7kd3(#^sNUG*{I(aZ zkhuFN_gNI?zvf)bX)2`1%#SPkCqk9d0_JK`_>i7JLguP)%S%16DV((zwD#Fp0e1>G z_$UicZJ#@KpHPq zn;ZaLsw=@tYmRGWA7SUrjR*;7bI5KDZ@6hgo4`~GaN2K%wf%i7=B}&_;SGn5jM-H4 z)$-e(bL-OaZTCJ%NuHz+goi`O>gAijgf3eRL#%ffqD;1GY;I<#@XX>{)T(DS1(=IZT+}^3q!-!>o3)Rj6N~F(zM-W;o)BHJali zp5=+mx;cw0#Jy){K=Dr|kN6aMA{t=;l_t8q>cuk;Ng!%Y4ahR1Iw}YnX+68F;IfTAAMwWuf~IwAKm!X8kMIrbJkZ``Qbj{s zJhk|HjDYeDYk6_l-3R4-HD>2V>66h2G%};GC^i^*Fo95$#|NV|Wo_gM@gn2uh{U}I zt;)()_?~k&cr|g^ZIqv95foxjJo6}lKr=stZ`6;M39||W{TSvkX)bNFe>;_ zI6L2d%#Wjrrf~m(8Gh5C1ep14^!$buSBPg}485c%HYu(3zA?LlMkLXorE@=gd?6OU zM00MAd9gRpM5x`68lata-)VCX`T1*29ckf)t=#=+#}u<1j(2Bc9|H>pUMXq?ftKw# za2a+E0PJ~PD&4VuE3yzjQMc|U455ozCuk?Epwz3WS{BRf($gH4XtMupUd>~+`<1V%3@S%8@8K;4v2V(a#F4X3*kqxA^bhpUo-jyS z<{LFq4Dj&`wmiwA!QO*-hm}vN$jBa5jKGM27qm%7>J;q(* z$RQ~=i*j_u$!e-}#YrF5VGos2V9jxu z7#a8zS*=e&Oe)Q7_J!yGJ@YU&Y;JA>;+&@7@dcKnUS0c;m)}GV&BWskP@Urnk2^rO z(hrW0Hfq?Rvbt4AlpgEr>l+(8sjjZ}YVTlkG5&;>SgV}CmN^{7d41_8a=Po8gPVM* z@ZV85)aQYOq_W_Cq7hQ2Iq74|P{cf{lJMzy}z zF`AG!=9|0T^Vm3f1%%0#!}=mM#(qUegeZRhD9OyXxw(m$@|L@xFHFqGl)IdQ?aH^$ zFXy_}uXAnmV#MzA>z@Bx>+Lv(WQN}pHe~J~aSDaQdd~L+NfH*Ujl*$Dx^_BO_@tPk z4&+T!bf#MVMIR_eINl(EdwyajItAI7@z3L`SVxThQixI_;`3rMX0HBi*380_kR0tP zfLWAeu9)k0{3rcm$L|>~^>6=d4COty1i2~swss=}uYL5sx^tH8P?MgMF@BDVb)E?v zTc20^^rRQXdk&R%0$`mw4CEwr3)G4v3#p@^8hb~!2|)yb5ox^ZB-I>2&x>E>Ro@lf zYXN}FYlOL3f|GllEs^F@XWdWxHRiA3;lts`)*ghUq_|=u&7c3Tdr48*MYnyb`MKAo z&cc@^?0S%lU6m#1`1keIcJ_GLPyGf|q*`9iU~Qc`FhD&p=;p~CWZI$vVlTGIV^+~q z%V|uREB<50PuqAaruol@)Y(>mL=*VDC>Zmxj)$sH<-O#?`(s`aG()OYQ;am5cx z?Ec4NQ(^z-cgz16+v_^aW03d#`jun}9gy7ee119=xx6!u6?*R5TzlN%i4nT}14aa` z*clwSEPZM<%NXCFl15BoeRdtXRcvqp)?{idbVhmHbnyvNP4envta!c8d8(OhA*u%p z)yAW{llG5$C7E{5{RWRmG{Wb@L0{k68a({h(DQqKOJUIh z&i@h7{|q`Qc%5-3Z{akpbuf2EW9V##;ArmsGslV|jUP6a$VGhY^MMGo-utxOJ~-)r z({Cgo@w?0Sza)G)OznI$K(OJGNjO<-Z{*@WUenSOdM~^k)c*rZHkpg7tp1woWj_GU z9ePuotbrJ-m$h>GB}u-_@*~pcC(eDlhLg=?>H(U!B>a5~WJ=+QooL$}S}8(LSF=w} z-QQbJTA=mB3DW;!!LJxg|U71DC8+AKX5^8K8Dw->uf#ecy(4u3Ctl# z{OvJOu#glGNo`B~;L%ELY(5S3w1I{vcd8Z-hHTP#^|>$fgZ1^&O{xst^{-D#uB}Fx z@F7i3t%&na(tf3{RZLt`>WNWiP^ ze@2rT*4*4YJUko{5)yFx@=*11u zPS1UgyET&NGplV5dM8Q9nlX5`SSEB`WJV1J(EH*nXI2jpV2V>zWd`8QiSpF49XTTk z!Z=o*M*Z)r{LX7%x;on&HhL13gqu4$R&;s(zenKkXHQsHwR3ZG@Aw8_5cBJn|9*a5 z=aE~{zUY&mVgZn{Z^u?Kw-=aroXre)KWp^8&*H?qF}P;5iF8c9E4y8Epz5-}A(Ck| zv;nDV(O$^v}X2!=Hu1&0|3v&B!YYF{(3f z>z%XNr25}bpoh_Jhl3=FxGan7sf%T>RZ>iSHIz}XNIlLr1WxM;T==sGNxUl}B)8EQ?D}^F{YeM>|D&@Zu`a*SRK9aGDi<=-tap>c610#4}XW$hp zvo0x8J;&%iDJ;)%0*3HN5N&J_O0NWl}72F{Gc zc!N#wjr!oaXi6hlw;w52@~} zp7T;754F#xx>4V9INzyYGj7x3)nrJsncTU)L?h&%_BBh_6bzZf4`;!o%5uQFJhOMu zQ-X}ckaxo|VH33z<~%YkD6uR@uqQP0QFFyGU?M>V$GR6B&nLTn_KuFzbq2oIzwgxk zw-@$sF8svg!Bu~K*1G~G{hysn6vRIwi7~&^M`Rtrh3`wEGIZsDKx6`key$uo7|~FF zXj7mc%~Gn|0ZR;TLaSGgLf=f*jQ~#}2ML zVdppc2q&W|%TTuMCE{_q>T}8DIJ4@!8(8!0KUhHR+gXEXTmPE#WINBAPo%$P&9af2 z9CDi@&3A`B&8)w;dAe0gH}xz&5AX8Nd1RDhfn1Y)l$i>pztF)$HY zJbnMvG3J!K`yC8aCacL}U)SX1UVqAcsI$-Icl|zs{ptUL8&h>dgWG|cwCC+2t@NV# zesmd<6zO0eTJA(?k-~$9^55LXS)B2qFD?Rbwi`R##CwV zubN~esnx_alb)LkKN_vYIB)*fz#FL*J1$hK=8xT5Ll~v4bIWW9B?6e_zcI0=gcYsY za1)F_A->gf^bDO4#GJe^8~qe$P{*U5vtj*i6J?~frR5-T)Aw;R_paUm`gwLv8ygG6 zQErICQ6}AGTF!A;yUJiQO=VC4w%!>rq!62N!!kXM8s2RUMp7OAX6H&m8H#ONpe_4NR*6wuQrNjq%wVxS!^HQzzBB#%}bi6V;idZG^K)iNxUGnwj zKNucH@OdxDlbdtfvQMYyR0t~#QR~tv4{C_DL0P%tcHsW^YsXpl|qYC|PzR0W4A^t-@j&hKZ(h3PN=gwTEdX*V!Y+ z4r{)*_XP4hs~%6)yEz=kUhO$0^=3;KiCl>p;ld-yE`j7g9khHTM0#>^EsdI+Z~WoG zv$$gP=LQ?dC<_n=)qp6JuwjPuY{{nbz&h%^_>IC{;*#hTtaQ8g#T7^_B3Jpj7ssf$ z%(??kZP(S-7r%Avo;hYaHi!)}GizP(el+g{Y!3QAUPlRv+&Q7HXEvOTsyg?smc?4_4+1|$9+Gp zzQ7Yb+Oid0a0}=i41qr84Y-!H&l{Ats4x_VaH$e+j^TTod5H>rOVV*T*?jcBS^&4q zi-pZ`9KcQxgQm!HZ8kiN6Ei_B)J(*QLb<`&6QLu2l;e|_A(|rw2ydt)5P(kKh1S>?_9!7ZI$|Nv*Fhc64u=e|t%VD+sjI8soUB+co%)_a z1!JeR1@t1bYiTYm)YobMmLJC?knVeBZcGi{rqB3RycExUCeSY4Y#Xe51 zJTiO2rMw;%;NYO~^)@=Ktx6mmn-I1qFrOq|5qN=HBU$pan{b=BG%IXAS!Te+uyJl_ z_s%^(9v+@06sY*Ye6-~1Wq0H0F#mADt|0P!H>f_3fEx7*zc?VXv`e$%tl z9{J5tug?^K3f3^<9-lXd;L@0;PZGq#fU0aSSAn*T`cGA`{LF@u?)MVF-^xY%h!|ab z8kE2=-FRaSMQ`yO>u&2(CEHIVADxzcro{MQ0jjqW7I;b5gI#GHn!~W!WMmA2Fg@0# z1}ksvd7TeF?;cnwjxVbI`AkVBUTd{u+Si%2+C#jzLtR?Xm8UG={II3yIkvQvvCGDW zKBZmQZj1-As5E{D7hpHQ%(?!awS;>X5-(3%i3}V0LD$1yAH>eX;cP|F{RWGSOlpT2 zg*HYJOCClodv_6^MN-oFeKhv-O~sqgQUJ)(H00lwTWS2>k9DB zF}u$~YQj+~J`9~sNyk_&MlT=DO6I)&YyGBGQpipCW(dx2P^wXYU)98*uoy;C+JT?( ziXN{~*zM5euh`H&!rbCjwG?TSagkuG$O9P&({lvNBeU2$2EspJf}2>OQGP0!V&@r@ zDS+sW216$JJOtOc)*du?(ezhY9rYvFK#JQUuPD0x?{RL?6D2cNrUEF)6FWiA_RN_0YaDdD%-gxSYl-H^>j(wax zvoRhi{79&jD|WfVzp?eOX7?4oyk<~2x8aaDqFX%&;=m1wBruwFCz=)@;`4UwF|7XKbs$M($3xj8k-v@5{DqZrh zItA+01?Jf1B1yEKJlk4&28L(@useV74uo+z{hDHjjc3KB_BMH18v{XBhl3aK9ov%l&D zvL|9)-mXVv#4j@@#GlIsI8@x- zmnfmq>0-I|lW*g1|5FLMIJdCh1ai7l4)ksPl2@%K7ND+i%WS~w@ai{kT_YniUgkSp zGn>F!5NU_KjZJ@}eOq@53UmWw%^iB|UlX1D<8`YDqp?|t-6<(~&kNSJm_Fi!83Pk& z>+Y&3kHsd_j$X($vZw<4Gx^rs$%E*2Ok{3b7L$(O3t1J~ss)75?iBFPgB)L)9?R=Pi?2bz<>B#O+MM$ zOI?kb<>=frH-d@dd`hPp1{;P43M7u#vZEHvqN{|sOE$ayS7dVG#6=OE_e!?F8`yZYl<$*v!cajSqBh`n1B`EGm3A8N2b}W_Hr& z+Zrh%L~>I#hsOuHM6u>sJNX|jm&xQQr?wl2_r+V=8WyI0ACTJjA-6`GlB7!k1|F={ ziI_>oaEiMs{l&+1#0d-S9i{n2fg!?j0_RkyjNC<1h2c#Ug{}0IE834`fB}z;qc}NY zi?jiWO&)zm(`aY%H&adh4j;IS~%!l4!2cD}H5v5yZZYHZ2Lt(y6Nzj~Dm}fWK z^PK7)HL$oYTR$nbuG!wUX78zCFn$)?amrJ(Vw9bHUZ?qc6Ru&$F-NN~RtIC~_0X5I z!b0MI&J1e184B`t>%99lEAsUEwAVPDg>2s9SLqkeH2F#d%P08NzNSWm(e>fywL8nK z9G4?~cUqUvC+QkT8o{!m&HwnyVlq=v>AgVBI0Q6O@{3-2{;d6B5oE&C_ZJ_KFN~&& zZU6L-t%EnZU~p;x(5Yq6UhEAs)x=MUJ#WvFkQ_5!9-Yn=fq)@4dY24j0`k}~X9p=h zsdrZ$cys&nEJz;AM#cF0qfet7MUvDHQ2bC-*D*4`J z^kT$DGjWu-O|~uVkEPuN6rcLOEMmZR-CxcNLo4{bPrPPD>^lAA<+F(0eTo;`EV9hx zCkWTfnXO2L9vU^Q4qg{pS8M3w@)fI@l3NO*OFS>R^t>d?;+Zb)qcMI^Buhg_Jsu-5 zGk|B)hp958Dk4vUpXJ8n3F#gp_^J49b!Fbu4L(+jRE7u#Ep+jYm40#pNowd5t!cI^ z+lWI;6mu5bf^(52&cvXCp1zL2Wjh_PCJS2IRosfCJ93c0483TB&! z5f|Dpvhd(m;y@D~SE|GW^DUBRC}{Nx_vTHhd?gjUIr(7jPjL?zvW>EtnXe-5t+=Js zw^9TdfYF>n;tf3=ybUFt@t^y9E70B!l`YDW3Fltca5sbfm)f}>dgS7!eN3uG6jt@u zUX|Xj#|r_jG5OYC;Zw?M$Y~1YP0QFshcb@q8VRA!jqh6$A3?judGn;mR=@4O*lXx# zJe3d>|FwKk&(XMO3udg)p*I8lY@!vKVGRm|0Yo7aO~IEF2J_7eG16NGPLk14pl+8y zqjg}kFwm;V2DKNn16%lP|IE^%PLpH?kJzdS=?=+(CqDXMWB< zb4#zB%S;iMilL92Z|{JSf2_BIop2tIyeWdL-OVgt}Y%3>fr5w%XlcRt#4mw?;moGXnH*X$HOMt$7 zWA6BHg$}(D^7eH8mmeJTh|IJ)q({f|klw9-J!w1qLAkQTs<(B&CiJjp;P+(hha{qT z1qf@CEKNDGmcJDxD0U7KkudB8HK z8*7P2mJb^9re=8)0lfi7o;-iB6zijCr8e0H8*g3pFCnYQ)KaaE;nCt zUv4tc*m2l{dKPQv+siIqr4=LRxX<-*asW01!5FvaZTqdAc_IRYQsJ~?m*h0)(0QWZ z)++y9J3hVgnW-aIi}DQFl&$-pZam|t71vuK05c&@Qve+X{HJtl{9p$Vd7k?VZ;N)H zXL{jQII2Zj72Taj*&`~;u12)mkBU!?sJzR0Jvo&P!`gL++Qfr8!xG^=$L&{zh@c69|-!N=3o< z#oe+menU1uf(>w-sGA#*q{6eOtk>NcaI@%(NcD57sbOf>Clc{HeSEx>EO&goYo}3M zt&R>tz#5G1Jo|Wdir+Ob)(K$J_?{CtXe`wKBW?+PYF!Se5}ncc_}bNE`|i#!9VejE zFSz*-=n>^3A^Q<;aXdf;xwsAN3a44K6Dg}FK+L;U%kFe1#hw~9B@N7my$2yIr$6^$^Dh-+>SP>mOm=NHeTwYcEIlkY#*Nx@vrNZu z=@wu{9@4@FlQ@QrGDm8Xa=cQ*%d%y}Ox+fB%ic*JTB#=i zaFvMT^B;1a&5K{Z=w>@yET@0T!b7lW?5+n(>aY1yXScV8bvEmoGmSX7l>Vdkp+khl z-O2C?y@cR}FQ&xYSAE?RD0R>gfU`<_pWfzd1nNy1dbZ5*4Ol^Zfn8Sj9IFsVcN$X@Y;&5p<-?0OI zYLdSsv@Ph8>x6YAH^ho$w%KWbn2Jy9noBj$a$sNxvq&ASI)zf!I&_HlonfU9vhn*l zq|_gy;2fUfita6ZV9%{2rt?nmKM;6!Hf1nNz<=QqZ>^isGt<57Kz)7(reTLoIG+i) zV}n^Aibst2q7%_~er{R|ShnO)e$k=XU#~qU5q_NFnis4qG_E4iu6*Cg*y@&@llHS( zPU?qe9NYGC$IHwNwtZ;YD}%H^5VVs*-h%w_xa)U2|8m_)kdg8hI%WrGc-$8D}$-g*455_Gpt#7}OZ1S8Q7fvszSC3)S*vll60J<%8_O~ag z%*F-zd$)rul@r7wepQ}}hp7i&gzw^>s0c_!+&9q;c#e%HJ1<)b*RF}p%yR{N_Ef~b z%`ewy2;okk`tw=UD@!ogovuDzyyyF8k-Bqx^eZQup|>&@UthW+ynLvim8NOIRT{hwK%&UXxRNd` z;n0i<^rtW7rWk4@Ra5iQT}Q~py21j4z5XOUkf=R(h(MR8orv`UA)*g2b(`CKJWNC1JIZAu+B-JFIfElCW`E?dEVtC z`ftwr>sjlS|IR|JV5to@>Z*i|tQ17Dm`T5z!oQk}sX^%Ra&Dp@r)C%(IJXVO4YTt2 zit|K)M$=)KDA4n;r+E_4yR(GQb{Ttr$Q$PVxZ*N~u2yT)=sb+jzEn1U_*!vsj2bG+ zo6BS|NQ1YWj9})z%4o6TGa4Wp=E!B^tW;yhzdAmz*Tn}w)b2<6Q#I!G-#^8%O;Lu3 z+F5}?WFHx9&FF15LYRF<0EH1?*eqM0F2=*St~jz`o7%4%1TnYSMF8DYhXzg#}(w}>8L6(z2{Pb9=m@_ljHS3LBanDUn_l4Ujt)K zY^A%|k0eQRQ`7zQQ~#m5#G>VI%j?hg_q+1!nRf0P88>>~R-TLfob_;rOJ)~c|56JT zB)-+Z7sJ$RmBVuauetE^qvwm<1RnJC8TzsTNBR&)`Zsg!ZovL&d5yDmezo0ubhSm& z8;cfwYHv+(ckloQg;^nY7aY`1Co5-;s%nQfLtQ74E71S;ZE|J; z+Ut!c)`6R&MdRTQce-uDt|wr|8&*dj1d-gDsccH1SVk46nS#wx!FJCLu`x%x@`Hwm zNz}OZVqnm_E(eeRLA4vb8)1#f+-Koi?iNEClu}q}%6fME!;JcKztwZ21q4}{X_gu< z)k{7YE|q))=Nrc@#Ytnh@uCDINfzUp2l~R(YPZ+wUi-Fh^GmMgztZqTyvL2K@9=M&{nfd3ea` z+-3_MtqqTq*8JB0uRZlnT45``OViqCrH^h+2M6VF!UI{=v3^I!JZnyeqb%)LFFxlO z(5L6Fb#a%c>-o%!=+{z>Pr1!6UEM*Gf=^d3x4E5aG5y^}qmHI(=i**nL1ltJK9?yY zrCgF4QV=K7Pnxip5d;Dc64ZCJ$)O;sDHkxjVVG`FND8;2KZCySoA?$r``KBVr%YpG z+<}46qNqq>EpAlVCB>hrN0!T{v4h9Qayh$|`u08F9Fk;xy`dBUxZJh7G}-!9szy&- z4I3WjNK7<$<9y8{-amH){ON7=9{6gA>lE%eKIvPj4!wH1CFOTnAcYS4UrD{3i#)D{ zVjV5jTfaQ_tA40os0aD5@}Kb^(UZSj^bSE=apq3xVP?>zrOlpNG?|Gj#~=O{6^ZNDTFW_@a- zxASa&^6}aK5kr=aa_`zJv~|mK)v6u>L48KeeIDx;`wFb@=-9yoi#!*KT*#fEs~c@Q zeqA(ffZ;LJRD{Kh8hk56)C_O=Em~_u?bo3tN9vQ`^e1gth%~o3>$x6zJx;~1*-r7Q zqSld-G?>n5o_EoqmCCA61SkCLiKED690O6qbjoQZhk(b0kzg+_@-1Jf$TIig^h$262*V18ly^uEn+=2@%wmb`eYr_DQ{SOag+;3s&W?gs7mzTreTGHCJZ5+N@seI;T zkGH<}*f??I@-r3ICCMTKgIr01=|4Ap2R^mmup+(BYMNNa1_m-JC!c3n#!pucV`47=2~(*otb@+5FJk^44Fz!$iAAGfaE-w3Q-+pQh9 z9Il;nkUpPjbUc$H$A9f&3Ae&-|e1gE3T2~B?vvkKVzBul&u`@z(GF9#7@^CfX z``T*@E)5^(LbdyvL+@JJO%TS)X(T)oqoD6A1CV*+Jh*ziymq%EaxM;3cwCQ(ymiG1m9bZQJ3AH!~y4t=Ak?;o9N9Wbf4L*8A`D^OlJJe)u4X zSn8h!K}1QP^$2qADmH4LCE;Bxu^eHoSYzD&`suu(xbP&Z1j<5XwH3|yzkWpNAoft& zLio)nDRbEfPK5E*-+{4BD)eRZ^S)3k4V%?^1{@bd#&4u^bCMxK2Ivks^9OUSd-~?robazR;Z3&h^iqUJ;E4Avs>$kzD!FJhw4^_n^qVGaRKE zN0Fu1Wc#ax#-HSl=4@L-)h|?0P0c%Vr^j`7jy58)$0H`@t+}b?QbKsF)+-rH>^Pm- zKJ?Xk&3i4@UaNhmt8y~eerKna{C0dwA23zGBZ`DHG=3H3WG_2ZvB zF5LX{`1o90>`^50*hA{`8d1_RGWb3A48NhWV&#p{*)1LFN2 zUb4O&=i&%b(1a}`*VWcB)s+=YMS(*;&p`pn9Le3#*(SEcyKD z@Zuo2`-mu^Vjrz{b!_MEwdJTma_!Z6;vt`K)wo~OhGjlU;(LRb`D6Jc9d~!PoehZ~ z+Vw?6{>v-2Ddo>z5q`h=s`K^!YU=QCte)1qJWkbbc#~hlm{u1mDfo{F;xwZ|^`D9$V29UoS9-G42pF=>Ek$q(9+k00MK}Qm+@fGF<#qCB z;bhj0FGU*6jIW7Vn7u(eK6*)Mg}~6#lJ1|5rOS@!ikYvC9uk`)Km;t z{?<)An}R+U=7x%|FA2Ar5pHxo_8Ml3sGvFW<`(G`9 z?-y%TnL%)Ot?TS{_>tC+$8E#xZ^V_86c{2z&=&0)vxK9mopi(?L25r*Hx$_Te+6~5 z=96|s>|z_Qy5rdRxO2C{kFE9OE!~>N2sIlUhfNnc-b*QVK+LeCLKjgZG!G zDDv*gM>Kp`tErHLkOWgwN)$Vdm}&|`>e|q6*ns(%u3C3P@#j`F#uI4HnnClBFZA(( zOT$t!=>FcEuTi~0unKx}fBE&tL7Uk0p@N%tBFF2gV77`IvsSxJE(h&251i3ZPYI@# zL^K?nJ|ZMHwe`H8T6C6DSAVOUa`$ij%6mlYmtE>tHMoyT44!6LUgkQV#{D`ww+*AJ zS6baqmm4QJQl^EO2(dhr&LXb`I#zW4A5~ug6z9^cjk~)AcXxMp_h4ah3Bf(MyGxMZ z5G=S$2#ZUw#ogWA{>?e}yWjoaTB@jGd2707y8D@)r+Wgy z54rx(iRRYN4AOgM$MvBh$-qG5zpt)8#+km)FOP-ngXpeEk?4$QoxdsI44)i(vobT2 zZ-EJN%`|CI3S%!XVDP<90qRJ_b&>uOR;>u3N{59cZ>F$pentWl9E$8#AQRm=>c%h6 zE-ga@p)%B1hiW^mF>Qq~z)uDtCyiiO>9MBZUEkz>=?-wdw4Kp6&CkmBU2Car4l}py zXdAuDB~fSpYuQ7cXEpO|MzXT>0+qdVdnEo!XO4Vh^kk4aAQG z(A7%tUM*VWvr?XO8&TVe*bWESdi)D!^n`UW6oBH}i*}XIYvBKK>gi54AfpS$r%~l> zXcC^fS&M?%yPtsWwPD0g$?WpIpO~Cnt}O0al8L~bE(q-PnD`7&pD>hlfm_cHK*9Wu z=c!)2V&KC>0};p`<)tzn`~~&TB7PQ^sSHjEH^;!sZnB{uX#nBd{&L?|H(gv0EQDJJ zc0&VO*4-i3d28&Hc?Y5tPvQPa9)Ok~0Rpt->!D)!8}zvQVIe9Yl-$y1v+q~KemwXX z^1%4~<-)eu9&LGRPGR~sjZl~=D)Ax`yDFn?fzY0`}>{j zAnUA^`c@dey+4@q$D9~*9kS%+LlEWZ2!K<_&~lq(0SIw91E|#{=s@;b0#3@Sv?kPi zjqj3&DsaWdad6VxkKe9l8GK_IlnyH~EYTn$&_wKryw||mI9)DNT@}1)4J7Nh9aTh3 zx-qO)$_blm+slsd7xIwiX}w$Oxjoh0+48yCVaaeOt;HYTQ*5wB?7zEyBsy7i^;0r! zJomfr`LF~RXk~9{d+0nvdaC5ai1py{!dY{veVGZybb=dEL)R<1U*fBj3oj9?7hk!2@^V5yF5Uj%P@P&d4-fv+t2H@7 z>>d^4b$<6lM44LtQ%S&00>O6vbME`G(0cQs{NDXp?zep8B$J&{n~o=K=Wr5|CQ%_+ zD3=|G-auzsWf(WgliN$k4Puu~?`})IiJV$;DK%yq>F_b2E_7j{VmJnKcxlM>6`ahE ziDL~LJ`*sc7Cj#tWn1^YIq>uY@3NFtQ14$ZHpOx$5_4R-v$w%#Utq1Rw6u^l`dJ2f zJrdRGBmYwa!I6C{TRQ{!h57esJPua#?>_L;4U7No;2+``Bx_n$$Ia*yp|2#__Y)@c z*2+JV83H0swrKrSy;IKjIBK~QAMft;qy87QJqpk6V`+%ipFcM=(Wzv+#Uuu|9r?v? z2TE}Zh~s2yml2(%aXN*1Ra$;Jefso?0tpkHLSEgFxTW}W5gOsgLKh`*bti{z(Q)us zQL~PMS!f;gM87dLU8F+8xt=a<1izDi?8DW@yhn1Sg{TP76FrPRW~@ z{^FDBkiCV6Fyv9~=sR}_SxZwwWS3=Dl-8Vq$XtD1!Nx!O_A@tI)peI?$fM9HnultM zseVHIcd5ZaBe_cZ7@{;badD1Hq@F`&g>URF8jk&YQPW1B+9>gO$jh?aw7d3f_+84>Y8nA>h6-SY_E9!L}XOt!a}1;ze|T)qwI+qyVFRs|MBar#Oa zs&EnrZX4_y9qbB<)NFYSwqlD|TS@>cmkcEjCOP=f8&f8`1|iRD3ovhbABS(+?)tq8 z6TEYp^jildIkntpdNj02F2QM*9QtJ6T^A-W78I92J0q|29MgQ09VOo7{rh?qa^;WN~Mg^2r1(wdiZj7d^?P}p}EF&Y3 z_W8r8)UKuMem2atYlybhr%%6jxr(n4-Ygmug5pUEZ9|2@21?1Ee>`YkdlCWB#uz+| zYHVd1FavH|L2Zer?S)-HIiBRaL)#KR`y-DUhCC7TQaiFUF~>U%o3WbNKrY#klIX+g z+(-!iya9=ZE60S5i_BGZ*UQMN)Up$`YEJ8Qf7HV+TD<>dNj0G1A8vFy=(_W~ezx5w zX#j1TA*S*L(8X_b+uwSH5jy|b8l{{1R|rk@yzFQS=*R#hC&rY1XY6J>Zq&RfZv7|2 ztxw#lkMxv@$ybaVMzXx@866Pms~+nzTOu}Rjq8WLU<3%e zCMIibD0%Or$#pm!84y1u`RN1_WSj@t>?z)6fRMc4p~GWZ~W=8iF-yHteLjR zAAdkM8+zs}uW6$16JeXjl2EkY8=n)hOm?07)Q)HB-A!LCo)}lP;7=t=^r`z|cUf}; z3K9Ay@6=x`y8XU)q?zo|N)2{w;J|4{G0>cC+iIe3X z3#q{Ii6dnJ7*=h18xCf=sJ`4`GP}HeOoU;4#Gll`2#7Y|gyQb>N=pFZs}&K6Jz^K{ zz~_1#+eo8NQpaR~gM@1--sB>GVrJCuVZhhrBz97r@Y4`v7Hvp zM0`Huh*|h~AFwDfSx!usGzlOUS^~uSEpv=nt$uGfoMd?pAVz`74`lT)BPOa>xa2wZ zyo<-JKU-P6n-dm@2af$3X-g{d@n1WKbn-?eQ>N2@X*!TI=b}Qfo)}1rOAk;w5n)Sz zxbe1;D(019#DWiI}fyutbTnz>GJ>SIH*JR@NuCg(Z|o0U&0l2usoa1m$s(h4K1JSu-oB zn7%h%EL6-wiDThhP=%8*g z_N=JwKukMxIQq8qD=6Xjs{yNKMybO*L%QykI``zX=bVmy=ut7a2&P+%X4O^gD{qMo zG=#|)B?I~N_MfvitdtE0ct4P2YjSKY4Eo#B+$Hl_S6+m7%T5h-ix$eUVtIg!i62em zo@X66qZ0g2T*+p9e~@ZbXqG6d$@x5JFt|oV@TyaBVwRu+msQJvh6&CP25yma*yZo)}eXDP!my{B`-&j1|3$&z^ zPHc)DQ}<3kYoa2e5F|DNRke>eAdI%F>{v^);0_-n-SFA`iRtbOn4o@C+_FST#hC30 zT}S0AtJbKhmMEq%>a^>+1rIaON#dU^{JeEodUIItxhs=tNrdA0IUQq|+^pH0xCE`j znf+515*ZUxdZ0-ZwpE@47dwmcG?Hk0gm=D;u!CUK&rA}+W{M@_>m!bn+&}OAp?!EL zEs&FmF-~DlrI;IEEU&ECEE_LO8w^PZ*md^EJ-1lUev23Azk>tmTiu;R4H4oqJq~o1 zuX@ZU$5H6P{UWlC^t*^)7&iK=aqvi=l$5Add4qN56wXtd2?HAkizuS2Q~VH`_3Gn| z4w+^W@#FOEsu~;``6nHw`_0y>hubjvI*(`9cSHCr;`}v_9(8|c|MMTvnqs%aByK0TA^Sft?3M`THPJyFG4+CfyVHgI+^9%ToqKKs&`*do8>aljL4qZ zRbO=2dtQ1c$3A;Nz`D8#L0$Xw{_&-O7s5L}m`8A^t< zS*xSSZntHomJZF_wTQD-U8=Q+74SS;xx33U{~dD$=`H8=zz8pN9(7ru!Wn_VDtgiV znu2q0exP1?+`=@|I3$(urQATT%Xe0b&!)TYHRdr-3dQFJTI}Vl>&m~&HsRy_=ns+? zq7-&Lw+`dm6VVeAWk~gT9}u#r;P@2_eL{oevPoi*Src&Q+FrsG?MPL@+|0XYj^Bq0=%a0Uzk-XI}KUylJQ8L z)%@jUxnc)NTHsc;3)YlFx)$C)iHP_FWqKR&TE_k{X;-zgGwSV_HyxBF+jB3%g;prI#qq@47bzF8nT0%JA^;xU<|vz=>o1(T^e;*XX7kyLf8P zzVel^-7gvp0@k+$hR>3wek~XXr;R)U{xE&6gjA0(wtW(?< zfm1qUUX#Bxl+R8$KoeFC)&D%lH63R!kJleR ze%!rh28@GY*G7&nxcXJif(F`E!gP1U8}uqqkcy|UhrzGBI%{xjE>~Q?&zA|UW!7x5 z{2#!zpWZu3OnNmpC`wE{ z5;I62JkjmLPt=z{dA|Wwis%kFySYWg^a03ZHRSF%r>N`ivMGHvQ!Sz2YjqNgxppDC zYQ3|7=3}$SpKo{^!VcZ4YBQoms=ZuaD(@=JyJlK0w84Vn)<)|Aj}?B!Na?d??2^X- z&1picCq=gxC%bq1!jPAjm!BTo_iK**T3m0bt2i0)Dp-x{2&HsXw*mYxXOd+DyZ~jt z8Ax^uTozj#AV;#^4>+zM)^n4r_Nal4FGs&pf6Rtrs48YOf=mKlQQONXLd9Me21OsE zBK*wU0hS>)Cjk@F2)R@9*XHbTH+HxZI-y!EiL)RI!qOCreKHPGx?6SF5nLNG%n{U` z8R0gaOy(2-o$ldNFSY;gEi4O3JY=kZIXRdO9LF^VZ{*s zV){8=oJ#tV_~yg(ZZ_&uSwS9ZPF**62|Ha{Uts?~@>g!@T>1!Zy6+xglLZ`r3k-MY zOfO#c*WKrh8QMdfUmuC_uEXvtLk#1^r;EPrIz6!?6El$Fn~_Q4#4L<_U-%IHZ96CG zv>}d%mpX!=eFKs}@mtr)62IK>GN?t?NEd~F+dRnBo+V^de+w`Jeybd15=H3y1=(Bv zAn9vu7`@fy_Z@Dl!?C>2(FcrG8ht6zO4DsRNN~|8;%AfsGX0NuSW#03`rXp%0~Ac@ zwuJ2~I2EZi2O^gT(ZAIK`X6Z1-uY!tQVgr2M`}?oakZQ&nzU7Zljmz5&?!FcsiO>e z6DJDwmkyM*KbXKT^2c1I5)^{x{jc0VyhQvjw=hV34hlvy*aEgkO=M}GtiP;+M+rVq zIL3lS6C}2M(bJ?C_!0S8uTNcT z)F0wlpZ8t(8(!A0N!SWqX?J){F4iNvJ3BpcWwsm~GLQrK=OirqFiRLS}$X*PEy~KIDVbY!`gl4o8rFfhjr+#ZJd#C1WXC}sHXUra3)u5#T zL;Gs32?8Pa6k9Hi0U`iuR+J-&K`=#c1NwBH?E$y2pre!1porywpXmEenP`^t)N3hL zv80{m%*cx`gFcR$#3c;FPfAjnmECgxtT_BQVXQPDbDnKfhohHl>Qvl`RqV8j$+)j*i+lq&Xm@q6tV25NBCa>^f3M$n-C7vl4jL%N_L zT6*!N1)*DfxKdWjigaSDMnNfWl=f(i3N^ZQcA&crD;j)d2Inj570_-%yQv4oABw%v z!%`%-XtDlfy5N@^)|E6+i`is6fY_2RrjNc zHJ{sDw7G;fvetGn2@_+Kv2mk)`90`!Ja- zgy1@1NR&G$Gs>QJf~PsFeRx(Sew+YeKSu#lL3n-De=CY^x-^*pZ|)?m=03F5O9_*% z9LN?Y!Hp%(v!ApulXza}U^a8P9mCzRRxTE)j~XcrjhG};*RAxVuUn72cJsw@fZYKn^iUGLh`Bcv)vl+4Z)t97OC>RoVDg>jVsgtD;>68-ool)lrAM@s=H0Rga%DP zcl#`z0d^3?q=ZEX(J|4oKK6T)^p4=&#Ru_tmNI6Z;lbp2jxt2?4I6)C2xEg2jH$7S z;x(%0L?lSEZ`q{^3iiqwY+`6p%M*7UHhigHj)xrAu?VjZD8+{vY3#bg9hv0YE0m>W z%*hbCYGDNMa^wr=5cMsOnK7bTQnBku!=c*q2yrloEfgC=I>(MxH!oA2%G_8S)qLc| zV0KIy`B+4eP@UNTCjeFpZ?~e>;fLfGx-@-;%l{)+)co!sS;Fn*7TIyLaSzLleh5V% zD&cT}TE}TDOY{t85DH2ejvFtG%MSD65O7SzYTV8I113+Y?TJNYmcWcYXLu|ZC^-(b zF4q>^gsRc_VCj!XHz1Vfy2f~W*2nYlaTqAa@*6Lro)MVpk`*R&y`N{@s(lD-K078t2sijqh$-B|tyI*(&RV#our# zsZ91&6x&rzAOnE$nTj+-koOkt;)wJPN|ru8o1MQgLR{0tLS~i@Kcg^#Vc zhGi5irj*&_hak|2=V?_5&QR>5AD?agmTZcoqznW>IX#88G?Xo)7=H9mX>(vH>meOY z6A!KkWX3G)M)D&!z7hjmdN;4a>Lldn#{)@wiQ!UBM(NBuMkl9&M!o;_g*aPH4ISUU ze&U_jl4kh9=4_qQRL@AO{^uk2XE_~ih`<;_Gncxgs0G+?SBqhGc({*%ty29Mj8^0z zfJgFy*TBz)=Ib@0CD*J7a{CW-DD3%@$hUm80+3r~Zus~_-gT@vGkY|94YIHIPc5TtvO5}UeB``#i6`e09@M`N zh8en-#7|;oxs>rbO9;1Fk~CN|{zl&abr|NNdM$aN?;le#y|=O@9gm>JVQBXBmu5fcu3vi|dX7%kucZR;+s5f)px8=sm8>nJ z=d9d=qgx~8o!0=YYp3~Wue!;V+|k7}j;5SDSkTmrt$u@lc+!QjIKDpfz13aaX7j!L zJTcTzAl@wtnT?#yRrr>p$CO4B13l4a(OvNmuJ|im8FnpD_*H{8++flQQ}?v^sVgNbOW@0eX5V~-HoscxXz>kC^HdWT#sYQ{6>;y; z^K(BH*?d#;hvQ>-GQSJ)wLI@@u3SgTou2#i0H;5ODH=$sY_OG`CHIOMtYJ`C$75s) z%a9}g;Q~B}z41X@@BWmy|MvCZ!{eve0-_p8WbCSTOd`@!KHyu;tA>xcwh?`7`Rq4Y zlbY6>senf{-G8K%cdk9j)t8Bxx;OjElk!;Y95DYs$myuMWb4*#;JE#`=J~w)@mmpg z<@G8cL9~4k&!T-l&-ccC`$~AS`?9SMO(lAZ4Q2snCi$y%I6kscr!8m?j!9tR+I4{#k?qpw_dSl-wwHs3KytCw{9&y#Brf)uf#Mp#xlD zT1LA77S=wX`ybpfKZ90#T3xNzxs}FII_UW}CD+rs^(J~+JID^$%H|X#3gjsq#AuXs zYRjwm1{d%6BgyCEy3fohx$+qGksrs>A^{+d!$R8NlhvBRSvU?3>~NDR@h6_njDv;* zkt!}yorJbsgGo-1blm0o*Bz>uN25kyP`WYWgu!5}r+R_Y4>y_Auh$t7SnAVAwqD0- zT6vsd78H(opSzkDpj>+K3iv(kcmNl>N*SY;xTb>oiVSyKQ&;*}_i$aNs!MWW7iUzL zw|w2ta+ZisU9_&+b|;SjDNd|w6D2T%C;WIaKZCyWe^GHRZ)diG>v`o_-0>|1n1OVB z$bo`E`&kmU1*RTEGZzIZi9m5=1S;MzC6lxrrsTZIsrCMR#${Hem}1Sgtd^Xfns-=f zLtcmx+`xJym+yTt(+(;+;s9@}7CD4-vcz6C(aA(bjAsr0kn+78bp!}@XR2trO)m~W zwoaqf>Dm4fGJW@V5;J>p5kf1pkrQEf+Ngb(^m9h(HhFAs$=gsI@R;>4|HsfiL@}B~ z6hzObMC;9PgL7L>vzYu3Y*iJ5fe4x0D$N=}=5_5h-|Ge-S8!EnB(FFaI2H)yJcW-R zr&cH-Md}Q+%$dp#X^B|+R{8M#Oj#SL-3x36Lvs#4IzE8T_P;bbvP+gm5wbff^W zwoI|bMVGhzHN*3xrq9rYsB{{bQmluhH70;{B;VIZ*CNz%FUucZr<35t);jQa$^E{t zqdn7E_h5@+Il0b%f61FfIISKuiu3{Yx0nCw$D!P(m60UOl*0F`SgSBB+Nmnq_ZvUO zq@*kgZ(rzR>gLT(gVP7x`Vbqko8xAY2LZ#ui|jQ^^^5jjft=>x-%caMI*nL$JQT?& z`#TOwl?BX1;^NXIA5?^#Pbt`%d;UH@w~fGeF##>^V8Qx0X| zajRc-)Vu|!_gqVt;se0hziEi#p|c&~$@Sd$oSE z&vhgX5-kWOXJMdNFj#igKd6CAZQti0$^C?560h@6itOpjJnObQYX`NBWCu}^A_Y0b z!}$BF^;QTVV^bXGhO|qxc@$L|ve*CqosH7*Xlr&!=?YeIr3AVFoc(ffxki&#(Al{` zc!EcO5T*ROP8U_1(Q^ksXUfz!($=Sq)-ogAr|rSGq~W?*{9p56?aVH1yI<2QSv3>B z8lvKlyqgA`wLXTC>u*O=vm-jFq|6})LY^z-qj2!!_>)35gRmm=u6eTGQs+h_g*M4! zA#;S)w`8FH=*zLor)43=s(rnr$h{l#8EbG-iJ^G;7H};T?Cw5wcbZdgm+X^kA~J7C>=&5G6-&^r`TpO4h010>=D+^c)DNKI zJN99+9h=;`gq~dv5X&Ulg17-{32TQukBBz56TayDm?!3x@uSxUqk#f@^kIa#4i8mu z9Tu$3AkcHlC}N{(&Uy7@w@ye;z;lb(Pi(kT{hAR}Tee#SBCejXi@c3x^kf`skq~{ySO6$L9?J z5r-;gS`o`!=iKvM=?@_zv^k5VnQT_*`r7{Mw+#W-r&e?267b=aI%vDUFP`uQCyC3?PT z=Wn`X2?jjyGCVd`#={!9kWV@3aY+oQgx18?xeak#Sw-glBp+QnPOU1DKR}QbnfAiC z3?Pa2n?XyFOHrS8yDIAv3!*Zyjq?stv0kt?X1#q+eUD=!>sf+oJyYgYkV#bRSXmq} zw&n5yRC`p_)Z9qKL)VhND(Zgq{yDpLA3ZdTq#Le=;D}&aB%}H6^K?+*1^ykkmSW%6 zXbA&6GO3CC)8gpn z25IN9icU3ug)Qp`KH_N5s?2Pg$R!SuW#kHaF|QH#>TT|&i8s3(D^>jPa)bb@E_$B! zLg!L2&Z|PecD&~%of-E-LzK3N;zPt6%og+X^RO>jK1J{21`l?g<~M5H1~os2Eh4D_ zcrt$W*4x$T7%lbSP~duFx=1NCYC$^xmqSDfA|hA9*O{VK83=X7-vlR-H{x+D0mil~ z&pQCM*ugs4Wu@Kxs`1t>#5j8pUy9p}9aW`ZXGWwEc6afq?^tju*TQovq)sPq_}|>D zJ@$hn@K}4RN%LJ$P~nTRZgS#(lAlx5sAv^p3M-G~Clp9qPpmuv7Xh120^=5*j`TkGOf z<9wNDzSqGNUq&?!<6q53^-=2P8E(ew+dFDK2}0pOH9`7Oq|L{Uj+z#^aL_|>fyWp6 zKjZu1m8;mPDjr6tF?GSkQg~}Lk$&o~St%!MGP^L4=searryK?5tv7uK$em#P3D;#| z9sR5zjj%m{GIYwWmiN0br973OSeu+-cQ<2}Ux?J)6LV>za@`riFRP|GgZOF-`xHnQ z{(*^D2(+|AE-R&zBwM~NT>kR^ikVKr7rM) z!2$MurM~Asd04?u-O=vnAsH*c&|CT}FO*+xYvGTH-}IV{I~+dVbU0qB!&~eZ+c+&h z(rbeT`e`W5w(8sQU6V37c(mXqmu1+-a|B9FYJr9xks96-CNjec=_M|A1B(@0F|2a? zRjJ`@FORqoEh}ESwXrjEl+~#axKJhgt8UwaWY5!IDkKqo>K1`y{*SsGY{#?~_^Y%R zu$O5^^@s*Y@FU*9sZGo@CWQ2t;G>1BqT z&?OD4))M$iKM6zhLYkpv=ZtSmL}(w@=+i;xxmsaE!{J~^&nPedv|4NWndx2|D(q8m z`e9q_L05LpmCAdh8^G&JHQ|Fe;Z$($-($bdrWM z{FA1c3tEPA^*J?Vma&5!Dz7xjT&iULmz~V5Dja?2261vFUg<77ksi$>kgABl#tLue z!dAHmM@udleRV|Rtc)%F^=%OhiM*-jaswoDM%T`>P`-DHz>&|O65GA+5f9!hhnm-DOar4U{Lezt=;YiycfM8j4?}JE#g>THVVc0hx zT_)&ufeLDrnvp%0aq|V*Yd!qUMt~i6^t^3Z%JeNZzdTEZc7Heu&14}x zNqFqjufl`Y-ip9lim_~l%#3c!Lotjl)ytqj41BgDEL==gj)5HhDxlF_*nm7KxE4B^ zTgzM?hJM@aa)hM!*4p}cA~21Tvowh8(%z}V-EY!@lL2>`evG*ZhP=(BaKPo{xF*^> z8glZMDQe2s2k;OqPT_A{(w)-UBETaH4tD>aDixrQrcqn*6dH<(wT*Jm`$bM}=c!k< zq@@&ZmZaTFPYtb6SUT{;$)oe=>~od?BZ5<76dvY`lJs6tf_ z!bnPbj78;OX`gi-pXF&N%~C1-m@-_I#ocV{02n6VOm{FlU-bOtSrGFMX9cCQVqwoI zB(y5{&u1;$!BMTbd8LXYVyU%@!QDQgU291l6TTum^G5&3wM&GUF%tmfUE{rzW_@0? zXKQ~f%`owr*!VROQRjNu$LHs&*4p6JAkQ6LirV!}mvp=?{_FUorlPLk^dCRX-(+Q2 zX}GoalNp*odu80(ve>_81wbV{f2_|m_LL$08@-}m$f1Bp9s?Lj4WPtf!udZIC9UB& z!&`>u)f@_G7W(#?pOnEn!rq@XMz>s&}Ecx%ntlQj}Kx$AFxEHb09(YpByF6g`ziq+vP3?gCK?!f^ibZs*l<$mfP$)^?nsIc~_6k zM+nw08yNCPbOU`Hmb}Rv>Ea;ulV945Nur?;ZojN|-BhrwZ5h4gxBZ$Qs7!^G|IF~) zIx2%|{`3xfmVwBByRL=$QV`(WW;Bv?d~3s&z*yozVVPwMgS&FRBytegNLpdm%Fnp&S ze>|39EebA!2-u4*`ID+R81iJfOKXPSO+s2MoU~d46sI+7Z>3HRXDjZGaAYd ziWCGhE^RL_yWa-!-EXOf&561PP=@JJ2ey+wOo3+g=~$**NFGWr!nSsPVA%GkN3t4U z{)1lsU7X-(T^Kq3lpaIbAt?J2zim%tTB0c-{e*yDpjC4!OjYhY|XhA zR!c1Xin2J;TgQi)ofyD;8H_U}MGEqO zJSN67kd~-+);Lx))SQRVm?k9oj&5#_;?ZEQYTM}rW0!D|G{}cSkOc;_q z92I$-T>0R2NG)c3yiY9-PC$5EDUg^eFk_t#aZTijO3&yps>5esj8kXhbVJ4=F7c9z z88hKa9)>h%Nx*sRlh&`5?V)l7D(T1IQdm6lq6&#Ds&FJZ5h-oHUiX>*@V*c3S^b4W zvG?IEg0$l;p14Q2mA@Ox%(@Ljsr>GJy5IToQL%dP#*iGVjGgZF1B1AO>x1Gq7VFd< z;U5cJL2X-24F9WBf#EO=D| zIW_c?v5bhQbf}AJ&(+J34%60m5&Z7WZ)83vPrra{V%^ufgAu6AH50Y8o1k5s(%%P& zI#h*KUFR1eFfCdxZQ5t5qki7tsw}MXPOcA(=(bK#{-2pli2MF#E1D4f;`sGtaBJf> z?fZW*7UI^%%Q?=wcOy%6FTN_5%=YS8Z^eq#zvZRhXK%Tkxx%W!zad>{hg``jBLtC0r8K9i?o8VADr9wqKm$bGEKPsGCgR){x}G zNp%0+iCOh*xu5=>a@Ee>HHhvR5D)+;K7Y?<8)eNDay1;UG6X1T=DBZVxKzuJ-v zeV{S(t2>pf7~V^_u4hh`%BF1C6*>A4i~}t@{n0^P=nxFUnf0m8w5SZrkF&u}{6C5W zLth4y=Z_BYQkLeLIlY-HCx#ys%$osF9x>|qy$;Q^4O}UUjag&ahqEd~_EF%&uPtHC z^w5u9XhDla+f{MzEd%mxS=gGhnnVPt< zz+D#syx{OLeS|8m({frj!{*PBm|Q_o1eEI)J=P8$iv7c>cA~UZsw{2;hm)_zL?PA{l3TR9<-)|IfvJB5mWJDX&1t8tFE>mza;1kJOB9w)p`Q; z>}6zb^b)ATmy^IoWc?OF_A)%pOBBV8m{5EO4ZRVQ!J0z7VXMzIy>2FB@1x;RcY7-yx5z)bWowJmoo(G+E=&cB` zC$>EEZj;y2mO^WV*oai~EID&Xs&pUeqkEHo4lL1r|25rPH=vTO<9rNGpOl%(i7dh= z&pMd;;ZdRn8SLsUrBF7pH<;U^G9!FJRNVO86MbE#O|~C$oKF&cJ|VPe37R|c@;}Ti zsnQ?RwY12gm2%BcR z4Yj~HnsktfQ;ei?y}gFb&X^*1=1~03g{c>inBbuy;cY;xS5LTZ@gjZkQ*{hRk5<3? z5hc^VtQl}3VB|9RY|s!VW{Lo?Tw}kCR@lnEK=e%;%^YnZkH@Y!<~kx#h(X2$v|w&A zrLCVBeAD*l+t7`mtd-?H%+X`%>oBGWXR|}Sszg>THCuM=`)#c+a&N#xkSXJ!@dK>v z>?KrS5kp|jF~%_zUa@X#^4F8H_fKy!c=T}WLwZO}gK3(-!l=K`&TD@<;V@8^=nd2B zge3TiFCfu<_CD2eY_EE@W`Qw2=ZD3YH4|z8tQ3<0Yn$ggI1hBpy`gi{W)zX*3KLp5 zRN%FGRT}BsFWydZF_eyVzr3qK&$8OrFJX8d<%8~g$&G0Mh{A;7C+lmcdE?e>=d)H* zl$8decSUu-xjR8#(DmHrU4hEy&IK*l8U+)tlJca*;) zBseVsq`Q1Jah)J8!ry1-#`iPt5ZAPbk9A(a!SM(CYOx0|03Ogh&zWczMO9S zOWV$Qt+Y+`c*q#GYdF0dyYEU~T%l-4ZbAbAO(2}1gTocK4a1`S3* zdv>}_5%aDMNlWMUPL1Md7yEyji}@RuhQKk{$F--l>3jdvf0nG*s- za_R=*Xc=oRtxn`yDQpQ`&WY4eHaccz90lE^^^l}w(YYGd9SWh28zmo++st{C8%gw~ z8ojhQe(BLq`aOkGP17mKTgzb#rtef%Z%^kh6oK&R?Mxk_ioISO@7I$rMzZY>iiz{M zX->J-?@r(Lru6JiXC@N^%-c|gJsDj{+aK1i;2;uiSSp_{Zd<;-e3(v5leSK6 zkOr2);U}A*NtMJayoUYHY5_0m*)3q!?q!Y~W$;g?z(QMQm%#YXWY4{h`ysM&#{^>o z7znCGDKdrTG$PY5B{0EJY6?7k7VioNh(-2ROfZycfLy54Y)_n;%O-v7C8hoE@Vz2r7lE zt1_7KsrVa$=co$oDh2Z+DgJY=iF)HW{M5Z_{l?)rR&orAMkTT*^4Xc_bu03+U)RVK zzt&aJDb1>DOPW6AIzevUzdv@sAwQgvy)j_g%9zIMCF!yO@J`!y#fu%cW(T9GX-&ti z8F_M``mP}kXQ@$>ETyOg*aWy-4gC<+{r37=I+G9rUOIYsH;%i{<8IM+%u3Z@rgOvn zSo>w_j`lAN88Z_n@OL6_g8n5|{>_%#4k-OJn6HqQcAS?10>uXsm=w^xs zL02@v2`rCSL{jH#)CnIs3vpA)xX&cT$v9}v9N)HTRcI3`)>D`>j&4YL>b$ayDAdnH zVO1`DE51%blLV7bmm@EqdoEca)^F4EaUwHk*YMw|D(>m_%jl{!gwojHI6VY$+bTO? zWHYkaBqr53B_M}$=yzc?b!ZMplcYrjZV5Ei1jPDdJ4|0`1K%G!*U>j>dp09(pe0nL zOulQ2D9@oLA5c}EMG;~ivl=Mdl?C%U-* zfVY%0R0HO$^HwI6jLdzv&#X_y8MjA60p%%c?d_lIE*dMUPhg69K-%5Xfdt3 zpRh<>|tJ0=-tjmwI8qukDNSo#cs>c0FOOJm$QYIxbIx%&T>F87FqDr1oG(YfhKq zDd?$S2Lm@H5n!6~AStd@49Lt4+V_B#a#&Q$Q1c}xXw*7`ZQ7$2BG42Ni8M^elNis_ zH$&3}RXcon$gifGt1#MAAgxps_dkT(juu1d*%;-RKRITWhgT#?j-w2r0dPcXi6`pc3nD;h+KT}9XP)|haxEe5+qkaNm zGNb$g@R{|>nrTWJoj8G4fNOS3LQf|TSEK+r(IB~KKDkwmFS#1JcG_APVW=3%ubZ|| zr|lAVDfQ4aV&>tye%CKW16w@|@lK0~32X*GQ+gI9VU7QJ0@CWK*Lpa&ZYz6+(QXTZ zmRhOiS)nB6R84enroun}P>_QP{rO|EBYKyf$nOj~E2iCiJBV#HM1I^d5UmvFR|o+| zBW2biF&o-%wm8KEdNqj+#tmkuI^fYhrkiTAGz5Z)+a*nu%lXr9%2N6BTzZ8*ZL=B!m6h(>FA~of-cbqqDa^QAMsq}!*a>AYI)~U zm*{KTzE5!GvX4q1V~#{bUu5B>O9h?0Xt!hVteXDfW}<2wq%r3R-S*FHQ=DA@^%m4& zdxJ)1I7fu=5vSTSO3rW%#Dw9$tFJA8_m;Ee(0c@WVig{8Zpbfl50iF z*0Z3YN7E9L!`^=WNqlno7X%bAF(mhz29ZH-$8@NIn&Fq;+{ca3k1aMHH9xmPJX!XkoLTtDwe1HS;hTr)2+#h(TEzXd6CDd?h*3Q zyYOBZHqt_FhuevH0*iiMOb($iW;N@zaexTjWYVK5k9ZPcrqeVONd)XNof!CLISpaf zJ8$!w_Fd8!wycW+;aqz)b_B~pH3~Puo8gcL7J@)ktw5@8L|eIrLf6)L8){K@of}#x zUNiYc(M2xjh364Mp2zbld<5}S!v^$We|G5E!atEYb=T){t3env%dT%GeO4SLfNW_= z>g)Is+43~l(Qp2t>Rn!h!i*7LM7116kWa%26%mbM7m<3ALV2haU9tYOQ(@eov5U8A z5>wO(3)Ot9wmH9zaWh|>YhO~iFjXwSX`KI1hq|u}i9#$F*lxk*mRGhm*q5`u#yQj9 z2!`1Ph0-s*p1}Na7KBQnr-zPCPo+o_6JOG0KL@8(7NRpoMM>`>(acml1a?}ZX&!NA z!%$!dHGUsTrW9&&WZF<3{Jj46hH+?v<6pRlEQWAdM%qOWMiW-sYrST;Z#)Vc@58~C zOXZx>=Qx!_rDB9jBk9SmwywO4Cj>MA84iMoqWn;@xZq zvriEPM=~-c$PALUu-Rrx)yt}-JJOlanbMa%w7{FCcgtEX!t$bQ8tI|k(N?kr=OF*m zRh8Zw%`krR^0Ta!Ox&zmX7o(E=tG zy%k2hHeZ8>e-rvC7T4eC44;`>V1*=;M_HcDz25Ic^n`0>!5uN+O?^+)VfucjVl-%7 zq4^x=DU967wVi&up`CIgHuOYTkJpJ)jq|M4Hl(jz*}2L6`%C+MrDx^(&rQwMmQOFr zP(}+Gp*ImOsx8oALxQQb@?fSfN1p^Kc2e*lQ3iDBe1Q@hI#focIHYAuXtGN`tyxUu z+c$jYbunzw?X0G{AMpvIJ8@_=1orX5fN;`*7U6WX$S9zDF{GcoHLhskJ7ve6*3a=F zUL){(W!s65^VZ5F(Lk;LL*iAB#miU7L5A!qFr130G_9TJkTqCT@%|YIQ5rdIj$ZJ< ztQk5emoxJPBNo<#jK4^!3x?Aupni;6Aj{5$qvWR5{Ms&Xk)f_;0ugT^Dy3%#$93*< zLQ*cx0aLx(rzH~UD)riSVvcAtB~=O4CaCO}(#E&c9oePvUwLxHq4C%un+f0e^%p6k znGt z!lK%i)y?9>4P+#NfHY51W-8-o-t%aYre^)T?XcvC)Bm+evH2yvi5L<)7}kPCM5k?v z(C5s;R`0F*H5pVZN{%DgLZQ5u4nhZUL_PSJOIRGitDs07aL(uZC@XP-o9A2|NyWrz z(?r5r7l%T6|Gd1%;cuQou@{&-_J&8WlJR3Q2-k`$rM}8uyv2-jdb=hQYuLEN3Uz6l zPg0fUCA3EL0t^SAZGX^2D;{E4Jmp5fP|@hyIX%K9oDDxV{(dwk4W7=-!^cb3TjM>&V~LT0{q*LzVu52Q4rB#g-QnvMx{nh@D0sv&m0C$HDXXSR zhFPvYToU1&`L<4Qrrd*|6|+#$(nyrT8I}_E-vz^*w4c?2vu^{VDw((5F1=+RuoHGD za3EzqrBe*Gle-!+y{H3WKPZ{qa49iROH|=~J=x)*vOuLG8C+iHwpg&(#j?s7DNnhp zs!?)jO!)+8$+CT_J*4}oilKBnl~M#ascvcb3kOd;YfE@@u@kBC!@G))1^{N5uc4_$ zKMP!E`S!9cYa86$_;s5~Mv!HSDYJ@YnD>rk%0NX1E9e@s-Ws089Z^6B2gy$3zZde3((2Bm!5>3r zJ~oq(=SkrUJ)EX)r8*Sq-p)-3hfKo7Y;Wi#s^cr6?`RLa9AuP|=j?A7Opks}RR4RD z;>Cj;$bVzILgfhs7Ld@gdSN>QdA4wcciz@W$oG3$YhNY3TJ%n)PGGcg&% zcS&P?qF7uONha%APuioZ}~#?4L+{D@>{6kh4E2_PpQnEJ^9Vv=m&WmwEv{HR7k zVG9aeR*}=3+Wi6dE*4D=2TsP#Qa3eB#R67)_8{0w)!}_*pv9;S!LC2LPSn0>TDjc& zW`!u??HEs%G*a3GS{>plg7rB8^eG@?0AZkpeV{{{)4v6VKL~3&PP@hCgb$NFh`#Z= zA`1g)g@qY3J{%AM;e^9!7#Y>N6~A|rI(RNyG{BHIEHl|A+SFV# z@`ExhQ>*Wu&kS0muB1k(r?H5@juJrwBYgmFxt+sp~{rp&&Zh%AA-2Uwfs9At5?50{-3GDf6KOmz!vvalXP{-Le^=RSJb>0%5=A&uXS zMs5C<*ZX^yy^aC{XQdRijtj}53` zKFnwa@uQ15A3@Z-RP)KDBnQ1TX9aySr1Ha+Wk*l^i}4N~sDI=FtLG<<&mSeU8Iyiw zGhDLoLEX~_pm?G$ zGp>hz@lE+`RN!2YKZfD8LquTkjDh-zmuQa-s|a?}R?_F28k>!>NciI&P;^!rYBfE&YdZA2xGzg75lhzB{6 z-m{9b_S#39NX|u2o>Sy_sb>MfxVC8kD-i4T`h4>XF*T%WT?1`EmG9*bL7(x#ds=^0 zRiWRzNGWPsKAq*ik05NI(Uy1WDRjQe`wsPf#7d;DzbEswd5^?zrAcrh+VQPrYH8Px z&SboV0oN8h@JqrYw%0hCL>y+q*l**V1V)fsOCo6+^a-&j-JhOICr z1DL8NNd6l=S{g9(Q)UjW%w<2ieAce=Dv0nFGhYib)zwz$zE?miCUOA?CD!AghXXh= zJeW|by|&`_wPEmMH1yTw)0N1Ce?tX!UtccW?2V&oNgsL`#;wCC zAtPhDswvIUNin+9f&s7Ix*pX;+x*LZ>*?WtR1h6^l_G}QGo(?i*pek@aZRnR2QL46 z8=wiETP~DypRlt+)}ej9GDP;5ZN4F+;8JA3p(Gdg0*l{6A}ZMV30_hK&fuWs`1owvivZyM4?m$P3on*-=ewW6(%MP2jFtykm~HeAez(q0h7z`vq|i zLONgHU4ZP8c=qkKAPj86e}CuN&#m@d(MRh{f<6GN&ij3wsN*{yPxIqg9QA%!3fhIrXV3PN6@p54kZ>*%hm_rFkyoSh)>Zj!Q=rb0y6~x zK@V>)_xbz#{qtr8LNqMwwk7m)jD)LFzlv)0AH(DVr|dSzc-n^op=~l)1#p68tO)NE zt6w0;u;OULiWX6-ZnUL+PnYxd8k_N6WIyluY zU}D$FxD9v>8c>aCL+4-aM80$eSfX^QDaRwWdsVQH>0W>QGNlC~vVA&)^**>VG9_kC z&#SqH9KnvB0+@**tU~~#*vw-aRy`#0KK?AJtGcppg&4703PN0d=#tnK@q3PU{qRRy zL%8J&3Ag&xad)DrRee@7?9b)Ji(l%0^dchS=@fHnY>=CNhN=KGi}ZR3c!Rb{GliYO za%~nf#b#QiDFE(`;c174%oN!b$DkONFe)Es`L1|?A4r<teu#U{0+|&wO7z?jvi`m==r{Q)A#wp<^3~ z91d}zgl|@u8!qlNrc{EepzX0cSvuSFn>Ty#v)eVGwjaIg%;)UsQma&P1kp;P*B_7G znZEvjvh(5e{^+*_A3AKibaniR3$!ZOUw;1ei%hmBDb}j)1C0&dP*Okc8ehQ64TY5S z?kR7;gH@hJ$7$;utx9kHhjL@muvYNC`U@Pfw{E(iY3<@S*pTh=lxSp-6r$qkdlDgZ zY~daFyeBI?2(TJ(I3(=xkQ3R>V_`+Zigq9e08mijCkxnJ=EFv>LxL^&84CpBb0b;1 zBL2^B)06QP{epMyGHf&8kkm2rp-rKiw*5=Dlm$Ab@cog92!zj4V%Qd#~D;Bmz2)h^#cf?p+`K9;@qt*6L_N<b3YY>4(=M@6k zeNfhRoW@LBM#=>6UCn(yq|hIO(a3nr$8$1U`m$(hcu#^ur12JWb3@t9EA4Y$LX=;F zl)RWa)h+|uSwRm;)}a?J3>0JyFiB|xpV8tfoQ(Y}wO+jeHhdE-ym8+j8|OQ@-L^N4 zRNPW@aB1`I1!XUh>&zo6UARj-FHb#?Eqy=Td%xUu8VYNe=STYF$dPGnYiut5x%QI( z;iQ9UAZF*@SI}rOD=*2l{ht%mhcelmQOY0!A^Ea(Bg#z2c#18}nD#+GS5UsxvNU!X zF)hh%fnB;|xOtoqMxsGey7Dl4275x_rV(u{WeB78ZmodUlG~Ur#UpF}>o|Y+@`lUJ9Vgu{}VNDy4&@*A1=0DP)NPg^FfqS~zh7;t%1UDco{LsnCMR z`^S22I*`$V@adyDZ0ChQf5Xm^&)Tih6f1gRQG3#wd^#)Y+@E@RF7_XivpjRirHMWOcIVWFinyrCe;HIXZ9m&Y z2|?P9iO{D}XUfoU|DvOhVB*3FQ2f=y?EXm>agnP_64~W zE(Ve_Ce)|zmUG~=#p_cgCP5J@0d(~P~xdXnY0GB8^lW5y%K z82ED10{Fee6_=Uo*7vjmS_Vuv3C(tit5x_xTSD1re;txaFe9$cGa|wmF9Lu#~JE%R<5}i}jzE>cZ zl(QB!AND^~45_wl$Lp>hpu#45S59Migj{7pL;+q|<^T&vn=)z@Ky;+?EF-APyzE2} za!H@lj0tLiemTx|jLUGIc?mTT$P!%7o&6quexzm2W7Q*J`MjwNpKu*zQHmRS(u7wQBmItTO4v;>zmdE69)iJ$u=_t$CveA^+K8HOYnCL> zgkQZX> zx0t)$<6HZMI>&9;;$4?sA$eCn>ZbWHJv1b+M9f|$f z>mq%whg-LfChK&DKImSPWn#$3Y^KMiBugJMo~+=3!qpU^DC|&;T~Mj(`_-H)#V0u}Xp4^qVT6*Ti4A>lyCpV@jj%QC z-15_ByD3MRhJsr+tOonImM(1f$21P!J3#pigF$E z9)wnXK0LLm(N1#r?55dfbs}h|XC#-)XT#)bA zO-(_Sixw?kVocV9wwjXnmNLnM*ikn%f_e8_Dt4SVTI-8r9_O*Z+Nl*ges=T5pN&-p zD~RMw&GtF+C=`5bA&}xTeDmP`i7;8lKrZA!h0`u^;*gNMcG0!UYdh_Yn7WnLVI%b8 z6v%MazW?A;{?pVLiRDicp|cXgyIm9Xbg2gsmNWX^T)2X@Y(MCG0wGC)Jduw7NEH5g zr%CCn-i$_DA9(_+|9s>$YL@PnO(Ks>i_3~3M^DAzcUSusS*|w7m`-4%hJ?;Tb4x{L zrhs6F+;%tCvS8=!X60dP3l#=L?bWZ7U2>Uk2wHeD*@sEtQX&Dpl5OE`MdCpLmmY$q zyk+DKQ^GimMdZ4Iz^>augk3HR@*SO5nY@XgU3cyqnv`%`bs`0=^vzK78DOk|_etpw z$zf{w3C|y<@t*#u-Q6yhB~5*Xaq}xn4npz!Vc0lV`>z+- zr;D7&7Wa>yi2GX$tG=~WkdAZuJ$w5zu&7>tl3Tot$wk({bbG=B07aUEI6f$G%sjHg+D`UxGFDF*7 z6c{%E2nN>MWd@={z~Og=Ag;pC>j0trsW&i8t>m8MifhFAV-B(Ksz)FV0^A$Nza{ql zU&;mzEZOOEu18@+pBT0Hj;3!l2s6zbpjy5NZ45C z2u-N*cKpEeTOr1SaN-@&j}~LoX~dP13+qdf*Wm*c3%w`D0YruKg$8h@dN?8&V}3Z9GJ_gSyxUm126s)NaIS;z(Ajel(gdGUXTu9~w zYYXaPZAH5&GFV(8yw>o=SK;?68p0wf8X%I{A$Y6EDfX7f#@cL%Fmix2n(G3>k;Ck~ z5tz(TK(EfCzEi8k|4~5nV+JHs5Wf}U<%w6}$8(m5(a_n{NY-;$O=?PiSyTriLixD@ zcG~cL53+QE&*cQnLAKL>RW1iTwtm!b1r-n`aCiJF+ia98^4rwcuam)pmbE%#d(jUr z{%%$u?7(A1PYp81_o5G655dD98||$Ibj%FJr|Y&Yk4vxxc))-eOZs!wRW!(Dp)q>L zlz6r2r>yLS>2}4lb<;2}wm{4Ouj1iKv{q5N<{l}r+Wd~Vz0n zXb7+0qDC7}#9w^D(e{YA*H3F9q~)V(mWqvA^n38bBX5)%EL$CSkG(Uvi`f&7?h64b z@Z!oIbKh%~s29i=*}}K#KHi(j`LJ(7dtT|9Z1UL}+x%>fk*tt>nqiXhb_kn5|Lueh z3EINF7?CAPEVG*QK#~_NxEVdx1uS>qhHlRns>0;E+>PL#T$4ULZP;V1ikD)f-H8Kl zgI;X|W038&(?7)$J+$f{X1y1(r3ggcI9pt2??K~i`1*(b6Iq?1Wn)2;3R(B-F3r|7 zRVQt3U~0k%C)WIXvnKo2l@&wVT=sf9_FR6bljGxF*Q2f&Q8pd*Ncu=gZZlcy=}`>_ z3Sydlo1A){lGNOm{{Of1AquJwkSla?Sd42Hh+Rr@MaQtbd#)LU9Ph>SJpZq*AT(+o z?vKGoiiX_EzoJeWUL_XMU$XXC1&357z^W zqgCMTLTjt`o7URTvBzyo%Sh{5Ypo*`LJz1_s#NAvn(vkL?|(_aV?RWET1H{liW&nw zP|^4nqAQfyFO3^}4PKl2CLZ3`?8hXanzCby+|s7A6l~t5#_exrri4-cChqtGMcFv@ zwFawKQTJjIwnGJ+tDXGO&DKTY&7?PSV>Fz2Tf>=-WyQ9sSl~kZUUZ%q|6$0#$AmCC zl7&+y;?it)W_=qYStr^_5g5bncUGy$!UzX6q1?GN)l=uJQ>-)WNXn>$I4Dm=p9jz* zVvk=))0Tv2!mmG@0+DA*L{FV(zsQV(E0XM<%Eig)y5Grqr!z1+b@ak_E@du`AVk3eL^ z^+oJHdW?vmAy+4_Ir3}Tdj7@by8-f<&$Hip>DaT?PQRVV6A%&M=Rbbx8ZjSuS{Kr{ zGKCKj_6*GK*XhR3pjz5!f8$OYv)^z|tzS zaWTXTLChG(5uns>^q?(G5l9o1goAAG;-NY+&$oZu(p=i?Vve|Oi5 zZ-%4|N9H`{Nn?BKc0g=neEBnXlxG&0oN26)b;HefE)0~3{*I;v=A;(j(HF@7z;>2D zr!T}as36fszaNt`qo<3i*r>ApgH5UW7i3Q8G42OQxWFB6SPI~YiI-<_>!ZOui?eGz zzn}pzab;GfSr=e924o5sY|A7%#%~tGJqOrox9WOAH&j}Q!Y6Vds&y4oqgLK5Euuh) z?5v#`dzX4(J-ez+;AA3E{U0GPeVw3&J@ zN3g`Ke(X`S_%2K_Ev0;-=kW40dhYQgO*$Vy5!RlBFF#rlqK$ zpl@uk>sW{|*uJ8{z5Ni<^(4&q4hu6nFeWy#xqRZnHHS{Y$5~SKzCGU0)OD}VuAUV< zruKSstYuP90xHC5S{wV(9L=CvwZ=h*B_I`)}I6pKnr#} zZY=&A!|A1B=cV8}vvU5!@xJMnLT&$+1!`k{oSE(U%;`C|&ILNmuk9Yi7#t075Lu!4 z&z`uMC#wDy59WJeCByaGO;~P8eCB}6jsO!?u_-GT-F*98{pN3kI7vxJDqDGQhXexr z*8n*pKO;h~ayZ9yAy)5J@8)J zoq0YC^T_qsvc_ebjapqj*a^1-86XK?B2Y48F(>%cALXaZZ~AyDHY;PY#iAx z`VwyFy;#eKCXRK25pe0`teXs))P+si_1cRxhMMB|WHfzPGY8ze6bbDjC3j}Fd3Q%O zk+h$x-VGNo^n95P>G_x(plTFTa6bMvn!omZO$gN-6vjif@8O4zLj2=R?N$b}KZ9>x zGGV-)V=6FvKbXj^at*R0irSXnU}QKtI3=Un^W@K5h^YC0&Rn4x~!#m{Ou*6t<12bHo zvG?+19Oe~~jAR8R3mrw(G<)*vu0y{}r;PLqekdMnq<7j8%)FFncOEKM`_EqQPJ$Zk zOT{JQKyNlvfN>X!?3Rd?>O`oV{pW|zoV;o$rE+=^I z9-<#!hOT<`QUfoKuI%mY?i_%EQy{))SO~KHY^jk~VlMMHX}#!5kmj<$fmCbbFOilMqKu>vq-E4P5O_8r2V2N- zm3M1W9V|i@$+b#}OlRK9h8@qo|EH}q#%3gPEiMA{aW`{Z5V)L`O5%V2(QgIYLYKsQ z;rsBC?>RdHoN9P#2Ear|46a}vQ#adVggqp}hZL%MuTIy@PQw%eiQFbB5VDpX`$T^3 z2fOQQut3vtDWXD_l(%*sRH7aIcX1Y*pyoEp((HZBfFouy-`$>0-8l3O(uQ@TryG|O z*5fl{tHYkz4P*a<=1EXz%cbv(`EazO@9>hYcP^)TTJUDHMB{L=gqRFBIm%-k8Cg%- zwWt3HT3a&W+hdvWBg56red{VE6!rsMOsNY!!>=+j%0E5LQt&vZKjqQKp$j)gPx{=n z_cW%YclHLWc*StZIkDNux-5Nj5=V;j9vAFqAq`>355j_7 zW$eZ;AIjpFjl>}G;YLJQn!7HuzHAcLaRffz+Br0GmRyfUY`d>3j0pn@Vcle&K-(s* zRugZ>4Y4k@wlaVXMxX!F08(--3%obvph5rpdKCIF27DI$>gH>hE>f?<^iCvS5EkRR zNFR9@m7R%CLXfppe-hu)|4!7AZ0ms920bJj>WNu&U`F92eGn9~`@gCNYTYjw~kU78J7-Sk?$*D#HzrV1ve zG1VW4R4$b^30KbnLHQ5oj;Z2RF9cYJUMO9YM4}oXwri?#oT{RqFZVtgx{PRF9;H*m zb8>M-3WKPuv3&*)iX7rlW&S)uU_5eXbPk2F$VHt@)@kOog`>^!(JfIB#Yo8cL z`6ecLG2X%6_IoFpmzK<2PQQEt1AQ|TeEXmS@!xA}0(w;to}@Y4CK)HkO3jBTqFXDb z=9GqQt;n*AZKZcY&j^85uO=pCOWFDm!#B<#EMBmtWS}*`d)jrAS3;{hN&qgOq)seL zu6OkOUUBWJ2SWJrYqv+D{<^KNsbg2+c|&A>)V!N#g{NUYIK~E6D++40RrQ#Gjvjq; zIgCO)*Y%4uTjP+T8nlSqr`A7iDsj|GIBLBN5a<&e5kdN`b%zA*{E#%^=#6>!g%1|J zB0GZlIe|49<51x`$5lrHY`D>x4QUZARpOmrOK)W(xxTc@G+{YoP%5svu>W@U()N|r z6We>87wMA#y8%0jDrjyfL~^81}SrH;4p1Pv?cvU^0{|G7bA z|EUM6TzGG+rz%J-acfV}XD$F(a>H|67G>-8dQICv%r&)9h9AZNyLpk{?np+=5hUuo zuu?PlB-ST9{3=@J8&(&$mw{uFl7l)@(h~bmm6xegx2oi%?`;eU$|dWlE$PRSHP^nu zee|RN!0bj>2jq0Uxm%YB=seOvPJ^DoE5rZJAkqidX6$J=VGauk$h4kPX!}dV8;;$IY z@*?U$hu+awUTxpB1>0%`_RyxlH1W|Ou11NJpF<5zXM!yZ0f8JK-j`6T=-P?VMI?sM z2S6G6Xo-&1{r=KlaE)2*E3*qG9J;eNtxuGgFEC#RLxr7%E3b4-KVgAf^#GF9=@d#R5l%UODc!5ahMEVB zJ+8>LkO=a6e0bU{hR|!(-8Zh1pzC^?olYltF(v)Qlg;nYALN7>Wd06K#_x~5X;d8A zjijSp6fY+&t*5JDU^MfS^=2xzZ-hhUgJRyTIzxzg0lGIE=-c8SR{$H?d87t=M?PKp zNqz*Da|(wS0W}Uhl`%xG>7{U8$e-iiPT{5Ad2Z>i(f^Rdw9HdNl8qv&Uo?MaiiR2XEeNgaEl-w#{}S3rZ?g)m zUt)O`xu8ZJThGOHJyI|s7nyNU87~M~Ao3h*47j+tkAKl1;L0UPB9Jc7vm*JbiM8`C zep}RKS5-qXGVM=Y`rl%&Za?GX9KQllx8A$a6w|UMS}m|WvQBeK-jAN(Ao8yFE)MYv z>hraAEKV8Rit{(-3TqD?7c-`g9Udo(bh4g-Dbo&z}3Sj*wesZ}adF&!hc$5#qGS$)b!& z3TP9xGwg_TY>R*>>&SihsS4KdMq*Y*7lug(^Jxaz9*gbLy|gE@;1UIHPn9jz{N zX(=D$oBfOTN+^0(pWab~C|tB8@a9r=?IGU(RM|C`_Eg^K?FAirt@>rCnp#IC1=j?U z#?Uemm;DA51SoEfkqC{?k36g<8D=V+hyzd!1vC=V$f6VG=cG~eDA9a;fJ&?gqG~{j zblUi3Xo^Riw^!8e*j;T|iT04{Mmk=-`V+P@J-^ccd>Q?_e4H`!Xs_#N<#`W#epZB`PGrWg@PTSvx~hr4F5Z1)sGp%B&^x>MQf?#KxeaceT#S7Y1SI z5s;VJ2BYO_9k!DtzHp}GG-^~XyC3l5=6xvTAkbW`Ln>la6A8;@v6Rb--w7Sf@h`4p zF)BB8xN-DzVJP;x;-R~Oh+qPla-@hzJS3Sgn}PrpOTwNUX*s}HAjIe%3dV}p9!1s6 zlSw7@FH^_9Ud1hf`KCViT;s~cjdIu6Qsu^i^)t>qNMd%e zP|DBwQ31D3G&SlfsvNt6nV4&grL|Of3cqqBJ}A)2Q3dN9$D$wU{BWrai-`V2ERrVv zEj;yR05_>J$ian#t|fiiW)7HB;db#9K_o?xLA{d^@q?LnP?zbu>Yg=gS{<6^NntNq zY|P*7Sm=p}3aIgVC$WQ&Q?K;x75=~)#eBqKt|-doQap3@4ou40FNA{@+p;t1xQY{U z@!eZ_XItskc;q|(2rjE&5AYL#6Z&++EPcCn^bOEqQIOKCZsjx@P)L&<`({eNhX zxrS{#$EUJo%hi05+>I(~QwN%Gji@y(X8oR)A~>jWHqfC5k( z;)wbFVuMBNtsiMY+#HuBPqyw+*YcCI+jcVM-rBT%!Db!Pr>sksVAAQ*uofeB@xBf| z0m2Bm3qlqvGBO6KaspC;-(m4G#{t=Vp$IKCX}z+j`TNyvxf2xh!wl7@f~?&rd&_^hyz~)1~a|xCuoLQI=GxzBk?BP z>0WevP1*P7o6E>ykcR?1<}sYYo)k_dW^|uTX()D0DlO*nWPU2)Q-LicqxIvo4!+CY zEd$3-jKqquR6s|eVCf7VZmvCeSYL;|P+A*JGZfzunLDQf_An6$8tV6`mBWe`^rzM_ zg5l473n4u?s2dg8+;uyj4xYM#_MT7xeLA}}zq707ZTFahOpT#z?Gcu%meftF{&kuv zuLrvF^T%imezdfThI?n4Fh%y|8z<-$rmuIP2vChxs4{S}*05}anpb5&bM};}t26Cn zExtbcX{RE4)Dr$Av0^pW?HhG{=BJLEydiU*QMWy%qW!kPb;YzS9qI;Yr#IAiq~_NlB5 z@_|E*L%qAZHLHki=pztMY9;Y9MIEuLxakh*BJ{S;ODN*_*WIgJ!zMgJd33Ccz@kTQ z4BaFl2w1eSloG5{AQUzv*Gl!EaC*L|T0hwf?`tBS@7RiGJK+FMcP4>PPi!AMtRLfi z)F-^1K?6LO647#{sR_F%RbM_eEGI|cIAVYn7D8WXoXc(EFHJf|9sAnso)7~`=R zG;IjsRNI5;eU#;%hHpdK;S5GrIMo!bc1Nj0TH{%|9Xe9P>9&PO6^Su-!u@-g5jEiP zSbn79g}K17te5uCI?Dzl=J+|k{^6Z+!<&;9S_)effP|&v_^RKy|FqqFx@Z)9oVK7| zXK$=>NDGjOM=`H>Fcw5O*#)O;#OmtLcU>aORcAM?X3g=OU~7LM8`5v%bavxOWUpjd z3rPMp(n$CwXt|CuoNK269_ZWnhu3p?l@Y|t1eR-DERwfP-7vS)Bt@izr$4)m>Q9Xi z#&PyZn-5jSWZrb^+kz?f`~r@ab39|2mUF%Q;;VN=T@d)LhHH1iTH~C+415q^OP}^6 zR`UG@F*{-WRB7!OQF4x{2=e*kjhv2D%q_&E_&g~lY8DH_TJu|AlHCLKlQdN^vcxt1 z0uhbJK!K?hDoa(7mZ2VhRt!V9b zrMX>2DvLtL3Q~xWv;fZpN)!#OrGZ1_u#fk}~d)i7(mY$LcWigZEI^~d$wmHJSJ7Jj0WR%PfYRVD5w&j%Gt+HqYhMmA#8PmDK?k9cpy> zS2#r1T^@eBa`9qLk8K_u+_yY6l8p)LG4hMmmd zmQwqZS&>7#;?9P6W7Gar4F80_o??wQscPfjo*0gB=ex4gUKQN-osE5eVlpa15Qq&H zUCje2GamA`jLhV)5+aTUdl&5Y!DqmXpZ6^`)$DkgNt@Bu-UM}(P<&3$l1AKc32V}n z-MD)GJN5YP7?JKzH}&9Odo(ukDU2i(WjSSu^f=5E8iqwPB4W`{CFxM)g`Q&Ndc)to zEL(7iL>1)HxdJGXTeGQ|Mf7rk({2buWB0leJ(KnHuxRJ1zJsuDEc>7 zR#o_ET)Uu!DnX1j%E?e@)L+E+r40;g_P>jorR2tyJc|~NAKM_G$WUq686U*8g zi}WDY<-&zIGZimiVPCPTt4jdG^d9q~9Uztoaw`4Qxrk|jAs(VXH;qrEtbD;N@d`)f z#Trw5kwT}>gwm%*sWV$UouyMcC$6smS}-j~bo`4N>O3Qr8dEVtj>QOS;W?s^3ax2| z$jeO?zGRwXfv8;&#`Nj}PS?+!NP-PVaF5(qxW@3UV!}`{;8BSRdQ_SeUPpj?w`izw z5VyiQwtRLVqp5eA7vkiFNE6%I9n}CEbK;q!p2K7I(^9*Ch_C$4(fDA)C1~c0} z{&Va8gA=$t?_p!pbrMU41rRa1YUb=-WwcZd1tvSW@&$I0DIv#=1-KxPVFTy=mL42< zY;&9;S~?t;NV>e+jjHw8_?*Cg8}97+pe#=^s9&b+wgiNDGww?_)MUX7V_s*QXt8jt z)o8Qt3ovQ@c%&6bL_4~arACMRXrLnioz8v89t z)tbsggH0*(=cY*#2QMG-5c|P&OPSXan+ad24^GIz>%PJev)t?7lvnB^3Q6gpz11Si zPSi5sn^YvfIGIl2TyZ@VX)q-RnHjQ}iERaE9Hdn5IFeRbvK!H|LR@CO zc6^cDDFI0Xd58<|)juTI7pYa4`{Eku;Jo~WZ1!z{UwiIvbB?~h2^a^pgX=mJf<@m8 zB0!)6zmLZ7|B&{1qOU4O6Ecm75d&Z~ppRSh zpEZhEzn2#L#x#)-=Pt_nhHS3kQZ~U9Dayt{MxKRY@u@J2>EKEd(_b&rA6jOZ$C+Nr zk*#Ze;ojX&qVTI6!yjK3Jyi&Eh{83Aur$sHkpbXhHyrW^ zAqQt;+WRkO1G;z_R2QoK=D?qK;GWlM1wQfzUeua!2>@EiMoY*`8o0n03o zF$0CZ4x)B5WbcZ}Z{QO6AJ*5{$&I_UUZqdzmy*%wg%qu<&eIg^BY}XI(62b*GXV zt3eQCpKAT*BfRS>YPS<3E&^V6S?P?|UVgWl&limqvS?AeYXp+pyBOJD%kHr%QWQttaJ#Hz#)+#OBcws1uyj-Jr z`K))tlSK$UY>NpTA| zS4NpryYLR$x_OBg9x71qKb+1NwtUAS^nBj)*y8M!2j6*{YM?-rt(K0ahm|c(6F?f0 zYXjGUy6@x$5F-mTx;u2_7ZBS0aAW&AO`ETgY@}*co8VMA^KI&j(P*NakrV2< z`rA_gZTaq?%b}*u0y?u*vD)E(-zpPnKXn$_;pd4WBQTCY@ClZVx57 z>XJ9o{iHmm$B>F0)_}{E(NU}D1&uHthdTA^wQzpfF*^*BqGuIcr+u}x+85RFvWkdD z{dUGI>XEm``HrdD%ksYCF7JI!_JY9Ka;Ll7LX{7N?{!^30Weo1-s?OFUc)@k`%pIW zkfAM9oV!7z$D$-BtN5@arc3$N(F~E!CWNbP%Zgq(Q|!lP*F&-8D0>2QBLb*7IfCBw zJat%m=s#91sQ+5EXpvUNyy52E5@2FWHzJn%d%*IAz+qP|^abv478yk&n+iYyx zX5-|&(>~`sXRUYrS3cY;-}e6PJu`b|u4}G?`#)H0Ux6b|d48?C5Pn~{8yf{m|Ki|Q zMC9{5j=twgEe%xO&ML{RUwV@&m`TCe@^?Ok^I_{zo0@lyv%z->unp+u3tV{KcDhc$ z70@xyV=LC>W`23a?|l2Y9j>)`(n{ODgQ~_@pQ{T@)qpEs2nCK@U{bz%lH74RnkE%r zxRUdFQeta+A8>~S^L1b?tr8yC9lbAdz%-JUt%n2=D>cFQbbrS80dm;1YXpP3A`a#c zFoxrkbF-SAfL{cG8!GhRX;Vo~_D=)z zz#dGTRv-80?}Q+d;VE2vR^}YBK`JYyh{Bv3z^2*RMZ(PF5QK}4f;<>;2pykKhC^x4 zKoewOj3Z-5<|56b``P_lQqFT>E=iE~G|y_=KsMT+a~PL@g*_x-$C*NEA%sEG(qw=sw6BDCZl^WArYfkDQRiv`AX-}iMCPqVLJjI!D)t87v!;f z$MF&-rNz6V@#h9#g()e-=lGim)IiBLjy>yPg#$r*~q zNSfH2O~(kMo*gw++3{{Bx+6FBdc9Th;%Jo(17Ao<+|Mej^BdaxjW34 zLN@V|^d=>Rizc!kC}=<+px2_J4&gm)pD0!yy|u1%Ew;2ODZ=5PKf*MkA*AOw9zrBR z3=`Vi#H`R;15wYJydNDU4Cs2Pm~Bh9_r|AQb_V4-u>&p3JbP}J9J)cnT zl5ti>89=xS)j-Bu{KmrV_mZ15pBmKK>QXc)z7^5n0)A9GAhNrH4q<^h=`1wz1b0Pr zWn1bQmz$>>4Op~`{xUV?s{wk?O@9P33RCxEf4Lf)A_&iL zTTv{VZ%JtbDqim!Za=rN;Bu(TTVkQdhwLs0hM8vZ!FfphC$ICG-p4E7dsliETzH<~ z6ZDI0Mm0CJ#`AH;4-qPW%a=|1z^egyj$E$okJF@kCM%w#b2{-kO2aJ6sLWV85VE8I z{R=PEZot!h=T-iIIuVJBq9^AOGaSge%RDPVS(Iuwq>Q7H=*pRf!?*^_q(%LH7s1ow z0dGlZ3z|p7tcWLY2Vv*=>c9Jzb^Vk5^t|4K#L2yQ!cOoswnQ66uq3483r~cDz#g() z-n6eI6xzw(B?Uc1Cd%swa6MG_OkT$AJRYAi8(*5pP`^-kF5?HFxJbLKc>!S>4M1st ze`oM@?_~eV{dO#AR{BqI`HrVlu2kNsa_l6I8fOB4-t#h=k;~3w%pMIsNoU2)6duHn zLu`URcoPY9(%%aTj5H`Dg9E|zq8%X z=~&8g>qpq9+BBAe`|jsWA$kHMb6%Iz(9gnNmv}h_`_n@?e0Lo`DHJ;{3?7&l!yV$a zM1%;*`?Pg+c}@YVZnWsn;U9dhn@FxhMpvbB=aLGWTLD?H%8pBIRqwCPH3IbPGN|ov z&nnu>NF8M1d0Th4g@xPo#K46Oq~kl7ivjUQksN&c(@{X^+;YV1XNqx?@&ZIcDtT~< zAL6i47NQ!F?3@YLJs~(?!l+VReQC>17hqtE8(AV7>iVV=V9%Jz%?!q&i-_HtmQya? z)ycMlIM!X<_mIEUfS3+pCG(K|`c6BWYz(n)Y?4BsFP;9Wx-{c@Nye#^;M}NFPV;b3 zwiS8kOaqZbWoqo?y{C|BRJx5>K#7-+KxVyDx*b>>w!mDsx0~lD6u6WxpJr&ch59va zP1@^XuL8B%DAFzn(HHSYbz+D>wGc>L_Fj#3I-xJ@{K+DHTIj_|3mRsxpMW2x@lXJ> zOv8cYO1B>f zhLMg@dF`sh#yt?sXPrW&C(qh#XQ4*Q;;QJEUdGBY?%YlnoGR%bdt5?{vN|hTIM5D+ zzNv#wKL{`H!aSY`|9Q#guJ6IEo?kAwbpj{%YkB>TxXFoRLh@kEM$sH>MH15iO)k80|_z#cpon71He!0wagxE1xDJyTDM)|eX*q@#JQcGuxD|AV8-WjC26*Bl#JrA^!BxcZl* zm7+uFfB0A8AN;F!m#i*rpr@kaZcW$ync1%h4)lf5{4pg0Pa!U+>wL5gU()aaM zX{DVPna6^4{<587c0q)x$+H_M2N=25*K`6l`5mukK z`JT6v2BgMLhtwjCGl{W&H-@=suo2dF!gvaJ=C@eIn*2;p`3I0x@z%a-(fM$xWV-Rz zEGyC3s4D4(;o;VKb+f#!34LS-r}{!=ZBEjr&Fi+S%c)(kM$<*b8|0Y2Da~du0L$Ce z>N9M6oXI;N+_T)gyx!xcz012<`{#lO<2Ftq@rj&PQh&h%k0b7Q#!}Hr)S=V_ol0Xy zL8;fdl-OYM8pLf^I~|@5CXRTfZbA2*L2oUQu}2yHhI0pfjO|_w8JlvO4Hr;>O1aJ1 z_rnwPNv(3vXqQ@tJWVD!rGJLv-EDvZ`$8vXkS z_#2l9bn}+R*(T#gN=gDCRLlK^M7`HYMmgxZq%;+LNuPGf8KF!C9E8a}el zO+Cs6h%Ws{#Qcjc4RQJX63ts9-wE@&q{!D{E*jv7%!uSGuWu=h8UnOuyWw-1 z?P_4)vi-D%^@jtTm&51>1FC@a5`M=zZbNXsr2-UtAr5&R<<8Xw?x4D_cAW&kD^XMFW!8WoiC7QQK5AQQol2;Z@QQHgg@b zLm4Kiqe5{*Vs#}QD;Id+|A!4A&_)gfteZrC9t{eadyBh1Gpay#(Lq>KiZ{s^geR*W z{I%Bw;)kdh2SSiHYaO;F9yA1II{&ZGvSM+Sk2|H{KL>jki6H1BHL_6Mh3o3Jc$l;LUh-JSfk zTmfOlKatR!ipmsN*A)jIZ8V-oKlbWTcgV*7hycUD|G5Kkt}!iHHl~MZ#22sfI=B~O zWhrpAeIX&qLL+&G7zL9?Ygb`Nocn+}KaYL>LykILM?zQlOrB3Wt(&cz&zr4??$_Nj zIibs_NZI!X=ZonO&T+y^jF72^%!+UdM&1xuTW^MC`m$&%t}C-BOYpS|x{t9Qop;*{ zk$9(G$(-nP-p?lxJsDFbcLcHgeZ6U{Bi+hkooAjDp&;@?H>CHpbMi&5XW)90e>2n$ zi+^s)ZLBbNI3z}RC(4zR9tnszHv}{cuA2UGpub$;4Z``o+VwIKjNc*Imj z#kJ}0{a`~7xxs$%0|WtU29I4X^91w{i}SzOhLseQzKy_<@0WZ&A*T45L$7Gcmb#fk z9{>0h^RZe6!t>Thmq9v(b zrSkrpj@HmEWkpn&;CiN1>eo}ujFa4lUI;r?^#+f`A$9ZmdLt&L2HLYVbrVDr@$-u1 zs3E5u95ULE!5GSz+?5QBndyc|-IRVP>TWPj!9Xj_Kt2vAAw#9tmh!96e0n)vvAl~= zW-Scx_EM<}zPWvAxYA(13&FGeJ6Ez;M;sM?e2TyMw&o$sJn2EPnI@b&9`n1nATXYO z{x36y^=kU3>@c1KvD3H29F=R$vT9eFa? z8G|vP3!|G7%YVohb3dLv{8WTk-lis6D4c|i&fJ2nPo07j{U9#+GE$H}zrmW%XODCy zyvA@!JwJ;}s%R8RIzYldaV@8>Ie*yLT*>t z&mE?e;7Sujz90M@b*MU+#mTCnl(f7bgJW`k#bxS|{Z`wwJPAwstGj$}d-Tz$61|Ji zD%UnY%4`epAG{q0R{@qLtr=P;PC+PaQGL9oZGQFg`J1KDfkX*#AyI7ZPo|*rYs;S| zd^H+z7spM%&)o!(;efcbvhp4tXMdVL-FFQjp@e#NJxoAgX{6 zffg;yhAxx%`B+#)agaTAxuAPp>Dc`}qq5~a>phJRUnc^YzWp6VukHB_osX-9y&ZJ3 zNGF_BdFN+cSMGyn{ycFg8r9O}G+g?vOCF$lZ41U?Zl;rb>WkRZ9cY>9X4t~nzd;=z z7lS?A70rDqk(W&H*f8GT?nl-)L|DMdH#s7V$s#lUx)Y2Y_5d(=R6+H|vlg8~#Xj#U zN%;M7hl6u&mvJnVgyBv93!#-igugDhAlO%PvdfZ#<_uI%&8HzHw!0`!4$Wi4O8=PV zmRSyFW zco(6y>`{%_EW(F%kA*wSxKzZx{;L+PiB!yP2mRO{7h_DnuEmX&3q?|1_|pu+jr)#Z zmH`iEHryIV9-=q#AIz8h4 zRx5RZ9uy=>U9Nv*;J0Hu&c1*nt^bV@lj#nZVUiz#yrir-3=-3b- z)~z_RT9~XkgIBeVQ8fY7oxqd%{#u5`6s@_Asl919 z`Aopb!2K1Rb^n2;iDN(K4o)K;rstQMqtgiT9P(2^^=)f*-zrN?- z(1!JpvmQS&!|g@^ZEW!_d)CDRHTD5*;Q?RI! zSvBxOLIBtJAAV7qYdmiVa^?I|+fndi@0y-=ZBiVUsM z`-3rs6});}Cc!COU?&sEkihp0>4OEgrZ5;%Nlmm6;DepkAFd#om7i>G6?;!Ft+mr{(VZq_0pb6Dqz21aBGTDZQuNkU11 zC_$8>%{p_W1`iE#EGFdW&8hJyHAr?SN^x_fzR+I)`3$BY4TrTuT@_s9VAjnL8@z zz2t5@^|$W`MF90_{+u-9t^oCEuEQt#KlEwhfci8%2j^I8kE>e=58LY=w0-E2Otx=R zS=83KEqv6kIoqtF3a)S0XtCgJI1cf6NoA}O+s_YtKm~=}6S$u|F+1fFyP;>`;NT z(?_uQb;BAZLmliph^kk#<~p_VS@uat2n&dru*&S@E@}t?E5kUTw&?i1nphk_^GD4| zKFZtVm()i`*!6h;yCCTi%Y4eWuY-DbPo@LzbZ=R2aiepb5ne6W=Q0046#e~p>xZ*pxUvhmEKeD%Vh=_ei=aFsur@jf#hsKT(&-SbG z3x0#g1fh~WFL2n{z8YX5^@IvYANw!!$OLlJ*_)JbdVDLlG;p3VL!!5M`-hSQCW;h_ zXeSyS%)f*$I&->gfbu=762ZZ){^ zthcVikr>GTB_5^KOkJ*Sq48Dsd_eX2)58=86?T~iT|eKtV^iqGhE+1g=5`A^lLWs5 zc``}i;oG* z(`mO+r1s?-q>ahzovus);h?_qT&=Oq#m>#;76g6# zA;@FZcEU1Gk3lOkcgF}9P*$uEo6vS5sw{^1SfebARpEaGr}uF_3foA{Gi3joAfC$S zP+knQ#Tc~nwF|EexG#j3czGLWE>o9Cnd|nWA?|noB_g$u>hWElXJtxfy4XK_QDVTm z`oBswOWh~T@fc@c5mkZ;ssUYxPr7TG3mTLNdVue0YL|qo?FeLG4~Y5^66fL=rX8Mj z=d=MHi8oZG4L7g^5Hs{ly#R1(W=hZH^x zbyZux%BxTs3`8kPgab8)iy*mw@EwT{cZ)e-Nkyrey~!GBw7(u~-*K9cNt@$&#<>Bq z>MS#ZLG-(u358tVicDfFs`Z?^$$oAWIL)1wUQZ|ZhG~Y#5wEnu7R$0+qi+(^aB|kuzj4y?l^ATFjEG*)a6Uk9U-v$&w8XW2^OEz^nCPlFVZiiI4a4z(2C<}m zK^L&|w}i;y3)0bIL&t7pxQgZ>L^hyVzVJF{IWa+9&ja#}dab$4RR8LiyJOg-}sAq2m0~Xhpx>lkn7UbiL0qv*}L$w?b$4k&=NW*_B!Zs zLSorvuM>ytDoumt%fF=U!xoq#l0<9Mzg%tBpSOGJmuDQJfmdbkasX%Bg&a>NlqRPmCjLysBcl~NMz{h~{O98{MYBKoaHDMgmvYBd~CTvQq6IM(_afktPnOC)4 zMxeNzo~&-aX5XP@C`6Ku*(J2`+zabz{810Pa(M;H+6c;;@igPYgGo07rv_29x#|4GhxWBvyaotDo-r@Y~Oo#q|vNmto zeg)ViWIF$GPQcj@5ep3_o(Zv({r>mAcwI9nz4(=G+2PIP(#CyJG*QjQ9`95AEpc(Wt&JazH0?psLnrl04F_D^{u z${Q8nF98#eZ;7{!&k#SdI`R~V8bVC|+lc$Plo=*%yIH2V`(G$Dn$$BS?U)m(%<1}) z`r^c#fJ)KmbNZ(02c8>)?eL=$_+@2;Qmn=Ov6t|9rqRbjn)hB%vuXPJ?pA|ox6>ht z(FDT+og;|oEtZPfana!!vhnJ0U`dX=(*lP;Hc%Xf&qLY;M5s@)gZ zfP@rsABN~^lUEZ(_p)>4Zyn$~eCf)10?H#Wc_ot5NyMR^o>Y$}BZ{-lSCiNq(3gYW z_h5jxeIwfiJ}7&vYtR{vE#)bT zM}lF@`!(NqZ5jtrpprlgX;RVEUNd1??3+4nbl$|O7gR-9e^nfdLAY^=5TmbFD>exOi` zW(Yi8@ikGKcF}qua>dBOB(zu{a}j?G9=J$*y5Jrw{LceAzN}qdx?qvjYx>X39C_)+ z@XaxhRJIsyjw0dXpuZ0Iyd20!8>zJvam1$%By!pnRVXDDE1?|}o-4FfXW(ND|DH;I<@i24tGlKqa`~ zFpt@uBm!b}#$s7mD1InBU(#ctMLr)J-Yhr#Z%)j~7rV-++r&U|oY`t&4xG;*Bn<_# z1ci%91!9TtCM~AOxH|7@u52JGb$6%&C~G3ODo_FVxXH$HB0+n_o{P$5(?}+Y*vU;2 z^?qXeKqY7q2uMA2@lL!xvE3@lgPev!;#?n|SyL*r#M=`PVZf){Z4ddsFPV>b!8oPv zHc-rmh#07y#8U`>m<*zR zP{wkzWHqloGa8v@t$8C8$GkKI(pkJ^dM#5>Pr8LgZa!5KPt=EeC``>si^iB_Dvu;y zDAA?ziO;;OWT+HZ_MVbPlT|q{aL;^+4^uFnxjl0=p(n)?HzY&!MnIlcT|&9>%D7{(I~m(A@Y}~i9n!oBf-s6E!x~XeH5Q;xqtRSy-tjH1=ljVGx<4R)C^+4-5Yc=s< z{NgGhn3sM23)7C+5iu;s`srwooWN`@YfVQ}IS7U>Fp!T-Mdx{WzV4wHzwX-Ua<0Ab zXCS%X@Z4jf`2-DP)Ju-_OK9jk`woY?5iZmK0BYUCN(EA`Di;J9J6XvxZMPs{n+QZ< z#u^VQ8sn^BnjV?YODn_$Z6H6 zKwKP!WvX0ZtuX*~U9(%sMS$`HTS*BAI*kxmeXH+5WgzqI*Z3h5o!^8k4Z7rTc89 zilQLlJqQ0*NbkDtaC3flBs|?jv|o&r9vB7QqmyxN15y+bhDQC)qq%QnIwT4HSpi}= zy*pJwJ;Saf8vBkF!02Mh?ey1gN#3+aU**V5tU{Xy;wBol9fc7x+2iL=Gb)yU!+A=>_PYN;xL8N z3fl61Ve&dfg&L`R9a%JpX@80hpGjGk1+NG&)Ugf{6dE+6=?Z*vdXeLTlp0g3sXd0Ji=iaEt_&s9;RHpQ9q z6`86pvMj^CvYSYEaTwEoM_^m+IVuGl0 zS729E_izJa<7@Sl0yacgn}4 zN02AP^lC!zxwPdwcPR(hVanj3rkcR~jsnHy>p_1xs85SPVIL+~8{8~~%2Kbi+5_b)&!dqIt<=RT< zCZP;+wO_>E1$DXHMyBk%ul!(Q@eUP>b&88)uwEoJ(g%y5&&v;#b>?#TkpHm-%Kt3m zmUhXnmU1}pHh&E-fNPd^)Fg(Mv(@~?sV(xQLHWtqSG+-cwXvrP>!K#q6cB<&s%6~cdgyvNZLF9_Meq+lI(_(GlxVBDp}kl(mRowOa+uwM15$x%0Ijws&7J*u<7_xW^4 zR(lt(gtZw+xWm>5gVJ_;EZRm!Xa4fEC|9W3d`&0vhgR0C+!an&t*s$DY{7UwY+>`R zVmlw<#@OvEQGKF#8@+@j`E=t%QNGlCC>=5Ew?G|l{0Oo)c8|!_5>oQCrlB|V^k_lSo==7+; z0(}Hwz+8PTs3G`^tI(QUI2fl*CDa7;``vMZA&5PrYlxcN@%{Y-p`3=5N_Fkv(u=6v z)$Rd7tfgRXj6xf3q+Hk3)Yjsv`v2xupq10YwLB8Ma;bgG%pJPNPcbF|IRiu&P@Cf( zVo^)}*7S)(HH8EKNbK5Z=;A}6Nu&q{<4vLOi3mUEkMM74Qix;VzXU3?q(msrD$G_A zv(^*^9RSM~)z>w2JV0GIzf*WJ=dWt9Xzw$nPxYHGbkTBDrMh0IO%w(F z5?=45It9;^_8gOk@k`3qj#3I1{?L{-X5}za`F{YKVQQfZrZh(Z#*vn-^|Rmm6$D9A;cKkOW+GI=CPqV$L_A9KU%a86M3m?Mt53v0 zUE*g)sc`K|m`qYCIfLD7NN%S|-RcUl-A9z%^R5!smQ5Ywt|QY-QhU6=_ezcN39VH; zo|5W|KRgxZ5mesE3kZonA?HZkPyDQxvlv z%k#^d2rKLaX!{v4Ze^6(VCjy^mkIhm!~IlTZSe4>k{tfAB3OotkqfQn9_?wgjXTab zGnvEA>A}TyHwBW>y-`9Kn7(mB6OR$ddnE41E|dJGrezR}8lEAcoT7CBvo9-2E?P(+=l`9!!#yglmx_X%cSSvV~X8yZDHO+2Cms!J4S*4NEnTjJ%&1fJEf`ntY!&rs9444|Qv#!rzV|IXsmuzsN-fS?a5#{k-`UkF;@ z%OiOVbFADX630P7^f((5NJtC_A0mgnuu4OG@O6ReFQqsGQw!n@_b-2bOLqZP4}ZI^ho|7hkEy zLF&e~pEX2Gnzby!O7|^;@X|Y43zG|s>r!{(<@94kWkub|)Rjca`eeVUfR?68W0`%- z3$+4Bhvw?BWWSqx)s70C^56Y23VRVw;2X>_; z;IBGmKK`1Aih5G-(F_sxXW{9o-986?T5b$K~NTBk<+=ohb zdVzg{Ao{;YYte?6+h4b$lsMwr7)vp9Ln;RQ*jYe05N*GUgvij;B$XL&09b5qi&B!# zoxa2Pyo=lE)B}qb0){BSTbL~&5r>P^b!C4NH4ll~k~t-9g@LwDlFAl*(|{-8w11SF zuxbHd*kOAQes4*+#eZ4e0KYWzK1K&O3O$X@Ak3GogLs6f$0)ss4wUv2>#bcHXw)jq zw(!gQv}xg-`@A3Xr=c9m5WQ$Vm8j$>3^z^_O()ujYgM<+_#2Aj+NMgR-cOB7MVynQ zJk_u05{;d(X9JZc!tK+uf3Gd4E^g(Q%7zo6G~%x&^#t{V3G9HyNTavD$I4`0eJLog zE_wzq9RE(7M|WzcWMsAju_+{o!QGH8oNkF8ObW+tK#aq$L%iTYw{Nc1N=x|;8UJRI1+#e_#o;&`_yH>&f5)inEh`&05>+;dY?;6AF!^@cco4M`&X*OpsAgW;o-sq z5R>EWC`CvUczLQQLPZ9u>0E4;KdqA3zim>EdPP|Jg|jM&s|d0uoBI*{N33^Lo#GxwwsN=t7d=OO4=OHJfz;&h`wYqgbF-yY-U9#(rZbXu7ba_Sxe zEp<8p=&CI3K=3Ei9hMsV7*q=@nFwK=$T9CU6IcVp7l?HthcU=NQ5cXia76^QIoLtd z5g0`Kn4hK)>BQJ%>vWsVQ`>IVXH37NOx#c6P4+#Fh*VNy*Qp08Hc^FJhY3$xs*=?D zM`FCBbzc1rR~j!*>C7rid4H(PZL_tD<9hlM1#$ zr`Iko!@qty+2$}rDM+7mz-K_Oog;@epBR~IkGMJ5Z>8NK22p#0 z>!00PJ8krAVP0C+m`lc@s%3j9NSF6-fAr+^VuwLDtyloACbG^f^P^Ml;?5ItFJgRO zYk@%ByU0<`nC=c&Kd!!Ti~ha?>)Sw1cG*cT zkJSE6kwKx!Y6yTW)^_3y>%@F8j`RyN>fRGFHq2tThc^llqXJxp2&1wy`h9zNh$ZF& zwT;x8T8^d0bilqD6&$R8Ed3&FvCw+-jfaHTG>awf=`eu~I4UWtJP7}>)+e;|^9dRZCpzCc6V|zC#enN| zTA06TdFrZTgN(k(6Yg+CRXM$?d%S1(!bH2uSw zSR1sW?V8S_UOwN3T2vspbaCl!>Vc8aVmNC|{oa-7Y9xND8xO__OoAuhFq7K$WEW9+ zSmSpuJ%Bq9!w3gwYR2eK6bKe(6#L%m*Mpgm7#yZx43L0OKtQyhz8fsF$mz77wc0n2 z?^5H5)N89bxkLLR@rQb>gWJf5dQV)PtBa(?EK!MSJ9%EyPS4^k*B0rs@gSEzuX3HB zo5Am=dWlEIN=X_tb2YVt)Mgp#hN-)fG~$s3?e2GiA}^;e60MT;x3thC3(##vk*fDt zCYdKM+y3!x2F$K3G7VgGXLwb+BGD8lg&GmvWEN>B8~NNdTe+w8QXGg>3461G*xkxg zRK!kgx0-`6cl{qvAGgamzoY?%HH+ufMdQqnZ>Gkek=fzi?8>F3KU{UAzg=`!3!EW&{T{;l&lNIBRHWq+?veT%Qd-j zkphs$#7l{>bRSzgmA_)>`^Hln9v_k5BKvcC;daZ^azIyaRZVhCblUUi;)MqAexCKt z5zo0S;yB8{tIA5Ez!LiOrP{n+R=7R!B!`LZ&>@$Q`9xo(-A%x}MFXzz$(;p5#dYo` zv_OFc%8H#ciT&1dWdy^FB>LyB=4JnDU$-H)bqex4f8CmA(n(*|6^O*pWztRvf3 zzH0#t0f_Y2?}VpFD=d3~Rsou;(6<|JBR-G2j!uVVPL}Zc7q$9U&$e#^K37RTTYTu* zdfc&z2o>lcs}A2IeQ#E?cC_=9Na{7M}$;_v(tfoq=2wkM(6-Q39YnHJX!NrTfnDX|p@vhS#sU$ks zgfuD~8iS3WXfK!!9m5%K$?S@vXW3LXz^kqP`V!#PtN7UX;5-~9*7tl^yuZHj=jw)D zZGTKoBgtgY@@fm{w@yHE>s`2XJ@CBSFKZOI@3Y#N=b1X(#EE*?0|$&posvcqGjC`) zzPGt>r1r!0Qd*^cP7jrj)c4hH_C-4bHua({B$=kOE#WZ=G2rWs>OS{rat#l-9Vo8D z1TyPtN0fHs+lHlNm0_~T?87H`silr~&YRs#{kDUZS~f~t(F`V;8`LUR>(qmuu3$AE zlU!b;Vo@(&^YPcWzwyYf{3O{+J&gRBCsF+w#pr2aT6b%Xc096@MYR;&Tc4V$_U#5K z^%GEx*C*(eB2JO)KUwJ9m1fV$q-gOj%ifF}M{*;lcX}y`DvDwSlFkp~6cAXsN*C$d zun=~}sVal6Gf+@2z^KCak#LS44Q~|wn62m#&RKk=`JeX9BgQZe)-VM^V>hvy#X8o+ z0L!t_$ON|^Df=he`?K#i^+`8&ZB+G~;iPfn^mfC^Uw6r-L(f1sWC+U>vCLS87L*o5 z6&z+A+Myny(!$cmi(gq{_;>7bKg~@l?Qkk^+u73T90Sr}cJpb1-c88hxy{gT9$qXs8Rhe7GjVYnZ^sMp`sJEtDRK5# z#y|2#a*fGQ!I0G9ywgh#)08a#=DcEV#yfh*#?hEHMzm?fv za~@0a=Lgj6=W%u!QWYDH#(p{U$J$c47944X$4sDrsMI#l%3gu(cPEH;a^Bzlz70Va zigDm&VGc#3J?Z$xiQU#EasDAOW39!sfUrQM|;3^N^)Oe?@O2X+P$`Yi`7e#8cmaMdCtf35y+D0|I-mIBk=9KKm zr_XvF0aK8FVYFN7%R!K1|GiCmLo~@9Eyg&9Azp7hypP+#ZC^7M$WkgHk-oU_1%SwY zT`uYNkH?0SyE6o_>G(1Xi7FWi6g+&Fg#)V4VkGhE>-NNoX*pl}4{Aw?%%;52PfVmM z{qBa-2LlBX3SAfNPM$8BQS-fOd`+o(axo0^QN=%IBfT{WY8WMR@M`1)8_pYw4I~jb53bkOsnasu9nHbt+#gADd$ntVPE4;AW+J6f@mYbj5Tu$4t45`-8`rs_s@{c8h{2v( z?^JCe0ommvy9^y2QGNfc6rPdd@a8G19do1+EQs${XtLB}1De6W-=Wv`&3MVje9Nr< zTDN1&XV~~U#kQv2t4~xLO{=xm%JulJ7i-vyMT7;C0qAZPW=yMN_aX;8C&1cP>S_3& zRh%>*D!K#QJx>L?FDcD1bZj#69jRvMqPj2cCpfs3{TCmug6VpDLF?eqmB!nL<|?*m zw)f45FL0Q{EDTmTF(*iTqImy>O(oNQ)Q;^80y659_w7<^qR)Jv>PYG!5k13 z6GG$wYk(tmfdfSd{dPraeW5JoqhVY>(gzD1Csr=hYMp0H5Pt6{_^Bnb_bUkRzDlhV|7n z(-43tL;`APY|2*#s1lM+r~wJ99a1HLR!Gqgi!C`O< z?hq^l4HkHld+&G7`?G(}?B3l~wYt`-szTa8J7~AH2e$*{0I^HXPMgcB=r$*nvaYPf zC3dc`#un5`bR~h>!kg1*h3e)aJNt(R`e zoY$y!EL7WW;%bn9%54xCY?`n6rAQW>6}7W#%ZGvWirugu*&@Y0Bs;w zoZDM(^`=A{i2;o@Tg1NkIWtqt%%9RPD*+QPt&Ki{;3>~etZpNc zJ>xe-9=j&II3x%$?9taP3ok+2E^*$3Qq)a9Reli>${B9&;C>F;rL6OZLa;E$E52wM z3uW*SDp$Bbjniewkyud0u;_^>5hW$@K=@mKIUip2DalTaeuUx2loq zex`$}>R4EjLR_lR`>)B!41>Pb!?wSUZ``9evNS?}5AjnG=I4^ym|QqFBYf@hKUU2z zy#agJ^DR$|L1F}Z)sSog3Xk$lcKs6Y6w@TWtv0=muq@tc8LU-$YM@b_>w!> zBYIG%u?>}+I_x$_M{4Opjij&r>_2k1W-S-}6ITe^zU5F6A9_RVz zT$r$$;)&#c)86{vLE|QZ+otb#p7uJKr>AN+i%}5hIFezdO5`M>5CE4MfJ;Iwju>vF zkHa;NxE3na%bXLquo!H=9Ps`FelP#}%F3cUHC6fC#1`6=n!}o|XhSDk8*1@0Q-0$x ziM#9Z=9cUk;um!G=fZ(BS0Wd21pGSkDz%p>uQ*{1udPPcuyj+Bm1(HP;3^aZ&L*w;JmC4)_1^uBur;~cOVty#e(Ru)uh<5x{q+4kG5s8P|PkHbM!@L`h1)b|No!yP>m%h*I9=dsatL{f7LNA?bx4{aI)>0%I`D{_T$NRlO5m4yDX-?vF8;p3v0t} z@ej!mgcx~0Rs2n1{xuq+RXS|8aXc8d)jTh!(BGB3Vu{YF_R0J4d1p^%!{bC&QjI2q zn4y-D%lwqO4oRsg`p#_Q*vlt~~r$k#zJMl70>AvZTqvd_NwmJHc; zp!D!^4mVsEH%^;F{-y=y&rTBaIQ1*@+ClMMP8n4)zL$?ZOFw^1b>$48#eG`5l5htr zy(TPkhBrO@MYnupY#c(z5yuxxQS=aV+t?Bse6`X>eCrt7B2F#DU`dOMZjLAocJvCt zC4rl4aIBC7IU2ys0#-GNGBT|KTfDC-%C>Bb8xs}GX_?RD zb%X^V15qjdaoyQF+Ah9oiqS^du>uBg^vzh!c)k2!D~D%Cd~A`OX-uwSoTOM3L%B+8Y9W{ij3~D@GeY{fIe|;n z$v7tM=fOw?yF`3s?|vXYv7j|M*T~#5V+*!$uKOkN5K^em#>{tU^_B|F1>7x(hB5OBbVW(@Vs!YA>@#7jJe$PZ&x@oL1mX^qe z*mI?kwa!9O)zXB}hoxImt2@=*%Qkrmiv2HF;sFMEzXzf@Tr1O1HoAY>&YMmkFK*f^ z!FafOTn(eOgeT_bly!#pDPk)TQV~`G6PZU5553`*1|7PX1@p_mD$h$vEiWUJ#v-;b z(?|)RDsq^)q5%Z*M*urlzFt0Ul_Lz#V`Pud&%6BLf0t`~e^!xW(0kj`b|?cm+WPaK zg&Ts`9oW3qz~@7QTI=48r&;G#W;z0n#i`QI-&YTkJ1V^O*0f=dbwx-k4g;4Ml9W~s zZOwkI1Zp}^HY@P+sb|q&TyPkGgE_I52a~eIYEP~xj)ApQ)Rh>7`PD6olpF0X2YIZ2 zJ|@3ii_}Z-NjU#!dnYd&s{@>?G5KT@kr!pXov9XggZx3H?VUaZ@3R~hhI{v;VZnURG@h1hnxWQ~mf}9o*8@65sTwuACPajHQFUCPd zF<9-O_fa@!WP(BF+Dapk?tf7_gZj%sIBu_%`7ryvDM79q zIe&)RK<0@k|BC%d5{bhm*Vn``$h`9t|)r!Iat(PVsAO}}sUTzOa4x#vo?{>{b`viO-WI(!kfNW!CE6d2v~)uiPog01w4O;Yhe#ex-*vXGvp zY0%ntYn|&HzM!u~9y=x$8zn=EFUQ`KKQ5p_L0?}8I5F6>;+6QYxDTQ<#40^26x;C}c3VU#znPpx*I+IHw>f;R2?Y+HRk9x2&e#v+}n|N*P zXeZ3Q6l{eWn0&TJ;X%dS=pUBh^*Ib3Tt^?@Zo7N2=oSzX6ciF&t%-<8W`NfV zA9QD+JI-V%15XHt$-X{DU)5|uH?8FwQ ziBU{=rDq!i1nwjD2F+$a?EGHaO~|iCw-bU{17i9thf}U6Cl6YkoRm5e${ydv zimeGx!VBP5oXZCNdJ1v}O+Np6D#$V5;%j4j-3#LW5Zg@IR?IsB;UAi{fS=O%xP;E& z$#OY(2rSFdGa14ii zJ_lEv_!-+wsFG-p!teMf$z};*rOd1bjhYw0(5*K?jWQ zYdV~#_|)R7k8*=LYmjHYE)hbmm+0^PITjju9hv#cW5~lI&IeyntR%&JJW5xl@g1h0 zD-V}zs(d4|rzv~4W3z%!)C8Ab?+F;?eIWe>_C3^o0AE;#*{AE05BGnrCFO%2KflZr zyp(+4zF0SeLKClMljrSWJ>}5hJZXUdKuG@z^2mzZ{CDGnk?&Tt!bWaF@AopeJ(3mx znjvj6rBzyZXV4;F+0h9DvI(Y`fx}Qz%$cIbJ5aI#Sna1y$zc#vDbvd}@m1lU${7!s zEHd0YHM+Sg>rPhqCw;BJf+}Opsn@Ol+)MwWu{U4>hPypACYd8@$bfpTs(*F;r}D(< zDU2kqO}~pM@U;yMf{YTlek$Jx2+Vbc-!a4gh*HP?#)Hzuf;|!=YX>P|8xb*>vhVC? zD$einVb|S!HMGgJ@!6#+h}<^%pUMJ+`+^szptS@pKFj3AM%1Ikj;gvJC=qH#k`1tta{bOS`PubK}|9%hzwk)LdO2p=-66JBZHlm7@ z$xaM-_U*_jMt;n?dWoVqz|;_&Mqat8yJRO15Qwooo!{gC(s0$ab`EgepMx=zRg$W{ z70gqoEM`B)(vccWTAvF#yx44PK->sYT(Zm~9+B(brz|CV)ck<@?e%v~ucNGD5&N1e zDDZ*kHx0tLVcx{RL?X7hb(K&w**xH! z6lEF+CucPFkWLesT&nFgP+-T`X4PckvyZgSd13VYzdN8^0=@>tTcOnfLYQ9(34C_F zus#U$&62K3a!Ag+AI@}C!38?1dpB_rs_bx{*dDoek~PF2Fx2Ej#NF&bKO-LQudw>ov-O0P z|LhE1v#&`%?oK}&Vv6ec5eMJ4%S+Q2u{-hV1*hjuRWQ!-{ix8phvyv6n&wqs+C{VZl z5Ff_45BTts0{?R5^y20D-Qm006741|fi;{-Hm%mBgd85^xw7|O_a*z*DXX&5uyM%_ zbB+1!RgDg14$(?^(nF+ed~Mv>p+dLDwM(rGA$oqdow4gUyG3-;Pi{YL>tEx8Xi+W_>O|wTH8jLS@ua8p_&#;Ot`Q#x zF6dW(Y-lEz3VW;8fAphvn>wa2?L|VGnt^==`k|J6r2>;j$ z%C@zGnihsu%L&>NMM{s(q>&aophz%A2lML!imyt>43+C|r+OPEwr-uh*@2ct=$ zWvnp5hDN~hfknECis)mPTwWqC-<^t6 zw$Md*7_SDR|LW`w(dNPHO~5@f;oy3gP=qj-eM|Ah0?uC%Gx`q z)~&EQ%5{B>_|YsQZpq)5l`TssN3svT-A3Q9Gvx`w$bois!%Y$Yjp|T;lSG}1Sn%3V zU{TEWI$+!sO6K%jUG3~?c!HaFwDFygBj~dJr6o%&v#=VL)nI_;mBUNk{9OHVMh;O# zE)rj>gu=(B_~A_PGpljg8&V>$Qc$(T(KrN(1L=F6R> zC_u==AF?L~*w<>`isMCXs14<27W^49W<*HAeS20ZzX*Ev^&ti6YEnB-;B)Tw7jPk*Qcsz7tBs!BOcD;A%NG5 zKA5b09n?@KlBnvkISE_-zOoR!Zepw4b>&tlEsoxSC#t%9?(!AGJC`zeT$6PT) z)WR#y_2jBZ$>i5ub(Z@ko}8vIsGM_(Vy?$r>(OH+g3rpE9&l3P*Y^apCcOIYtHJ_2 z4{h6fgX%cH!^kz;!)7y%D9@(uDwSoWD8P}MpSy^{;EPZ%t@00tx47-$jg20s6t%$` zF$?F6COa|+;n=+@ex?(C@68c`us$km_<3&?eR&T_4QW@^G;Tii#F~zE9;l&oOPk^% z2WavdgwV-`y6umW=x2}(*Gmljb$;>$L>KU3vhfT{M#065Lzukl$Tlk%V z7q;G2$m>wSgHIAmvbjd?8Kv32{{Zz8r{XvjCTy0fgM)yb^gF!fI5<;r9mb%YZ>uPPOYX2eSp?J{dMWW$T)bnTnw`6)Db83B!tF*0I z9wVkGeUOTuTb-dT>iC~7_OI+L(FTo0vCnx^dl&~U0VXDHVnS)g6AVOU$_NeVC(bG< z%88~QWCygbpHtU_nj=*P!eAOX)x)$Cmny;EP4!c`W{HqQC4;}6p~ZjZXw5&w(!kF% z3N^F(GQgB2I)tF218>>SsiO379nSW`P4K!O^hX=;I;DD;3tmBx|B0iaC>39FRx*Y- zyI>>93^U`y#8Kg>ga7f4T)_ghDC~*(SgE9YGFGA_eKPXFZn9_V6NtN7#LYoBo~1+& zjR>6W69c57p)PTa&``Fkoa*Mx?r~;c#SXW2YWR3@jiNCYAY<4)uQv2o0R3Mv@S%6* zduc?n6LrvFUCPkmKBoz2Grr4Zm8KX>wp8trtrw@F@T4T!NKT?|L3_l?#s&plQ3l~E zQ&X(QJr=(frwZ)LM^|XLBKQ#i{$S6Xe&rBE+5I)nx4%86_u)>k7A4Ij(idPg2tVCa zXU8%pr_dzeD)BU1F3J&SNeu@5*m}xhOC_Ps)0LT^wdPD6_ooR>JdB@J5{FhD>vJe1 zBd&PUuB0{DHQy&Udab`Sf;~ZldlP~ zmf7M^!ikDPxR`I91*n|&h#3i@(E^EQ1R&CgMgE_;RG_=S zZo>LKf!qOZn7EBJbLS`l)-Zv&8s~%VoU`@s$n1C$t04;k)W>O|zVmlzHrYAQCV|6z z23=ZlILb^}h&XH&gBznwxn6a0!o@=+beDZYD;Y1-gnEX=$_q$ahVEaf3*$?5s<}^X z-UJkwI$fNk9ZrT?g=OViv^RKbmu~)2kZQYht)830ucF92L7%KWw4zj9z3g;3`A^R0 zc5GCc-R^X%u}9A61LL~9clt{b8ptKzuolo%kvkfkU^-| zIn_!6RGRI}0W@5*7S%1eBXGCJXZci7*t%J%`ve*R4*kg;8_2muR- z!SqA9Sfo^CQ{B=Qa(?@s$+7iq5Z|znANfDRj0P~#vPo!1idkC zgpU%jdV(Fj_ismH-Pv}3m0R!|m}d7wCSEc-ZCW?0YDyGMg-0nk0a4aX`mr|hZ1sOq zb_4^EQ{edX&|{QTU=7fB6wn~pNEf}YFBFp9c;{QsZGktxv%_TVoO=JX1_o+2oO*Wy zYxiz_TN0M-W*XPFU0io*o(7ZPs;)ORt(i!tSd2M);1Thtk~Mv(5zN~r7K_@tjF3#b zH%q;V6Xr)UTsp}?HA}=)5BD6wpFKwab!|K3dpcft(dbRdey6oA2nj|C!->g891g&M&1PLnpSoB?G7KTj*m8Q@L^BLwZFBq(^&m4eP0-O3nHu znF>P(dK5iUA?c>w`trNRUp_=4?X?@!^Fis+yhtiSHsdmdM9oym9VLL!Zi{LHWGPW`gOZnfj4mX3WNR!OvW88r@P`0Ti8@T zeqIMYUyk&y4X&4*Be!)0`Q4qZ)CLFwBU=-e9cuSHuG5@Up}SM*Ey5ara;H(|lk3VI z>xL5}83v$roirbj9XTZ6$aUL%=zpRJV^`Kxy5yloTp<7qZ`_4C1#V!9_#Dm4o|Yha zp&SvYw(cBzcz4DgG5xmR%Xf@W4LBJycdd-a>z!LyVX}%o4B6m5weA_M)4$y(=G1?Y zQA|lSoJTI5vG=aZUkc;Z5>e6K%JH_xtN3a?Ox;*9wASl~03`QnRU@g-ZvAUdcV5$d z$oqe9vs>!kBKlO(RvdIk0yNET39gr?;XW%#P0^l)qDAm11LURU=BnUs?~ZQYmad+5 z?@Ny0dAjL2(422TAn#>L5=qn&oC=uEwa^=kws+rd2)Z&!waMAV=D2MuBba#hiU)PS>y!XJ z&)5I5&d_*$JG>h%>p;zHv+o>bsYo|AvT}Jmb~va$hFKEX?2#8y9gDxMb&(VuRjr;$ zKnxefn~>`U!3_KHhfjtU4PXt=_2auwv9rj}u~k>AI_}|&*dOw+Mz#s7ikMWj2$kU- zYA5LI1Z6-T*9{j=1}mgd&O+^4ZR2)#in(GXpY-HuKKx{eKxCR@n#-5Hsqweup#*tj zug+5;R0r)EqGsvxT-v3I_wFGrS~yBcZ;WI$OZ!ni#`skO-Benkf2DX#Z;)8^ zPY`zE27Ye4MgW9fdvc4^m&OP0`wDF4mNM-%8=%|05%3_}EhSB@x=C>pT^SGTcA4@! z8g>~06N0C)27Rexgw4&+8aT^IXIwpl81z|kXL|4Pdp$wR)_wTp1TFYNzA%10 z4Z3wGKY{WE5x>2H?%a+;&(|*>@7kc}zD+xt$ZvhdEJpd zqT5|GX%h@maPfb@=vX|Y_er3JBlS5-7QrijD;5RQ=h${TGGdyYkl*cbyMyRsQQSVn zADp?Tk#yz9Z5M9qa=Ofw2KgqD2%N%eSRHw2?;t!`aaRebyutCZ)-)%neLHc>=kv92 zz;eHei0CwL4UL^z+oNL8W(DeV1G@A_PH%0Y^84!ZA*Lr+C5rsW)>N;I1 zv-=ixpik;2lkw9dOpRp$hPa}7Z@z{aAJzp~%~suJdDzcC67JIyCL+mSx;ToGg^tJR zd(jRU+;nX{j=tepK53@vROtL^HYnAP)BNK`QvVEf*bWYV-|_D^1YMdfDMmKRNLhFu z<+>SGT$Uv-)HQ4LiAf}TcG-M=DJGCa;c$iDjy$`&)c8g|i6cooGAq#L>l=tO#nS=I z{Kh@Tx;gMZ^o*;@pNzouTJF3DcIAdH+iln0D)ULpNcyQ(FX7=)?beA2>CVxzYN^}$ zMd}u*iVPW5=)$|`542)I@)XJ|*ju@SNn1oHfebj24`Quyx@kl176#yzVF6smFC>(j zjKeW*T`o#VeH>E3ZEL5KhhMUUhLm06Frzr;7!TlN;%0rk_|33f2fhU_lu&*V|Fb`) zN#Cob3(_(sB^;p=$2!?ZtgcWK#Q9Ljaqf{(eg959pw_T3D-EBmprl_=8mNwTUIp>p z-rvVLw9~_oq|NDV<;oGwlxLpw)wvK>q~gOr=g-K%7mT6^{nxHRdu6~caBjbaj!f50 z;m^t|IW)o7ngo&rdDolhCRdwkHFUn)_x>+8Ua?DgX=gj*1G9e>QC+y zWliV7DT^x8E#u;EY+!jpyEqZmG1*1%zWWkJ+mdC}R5NienH79k(0hxYrQ120P~taY z{T((vv-m`3V8Xv+(b8(PPPf#u9K%MF>%Zigr;KUCUbyd=Ah4bS_z=g7w()VbCUVmD zfEZJ^OY0!iAm5}br>=F+laG^1fn=0oB?#g?MdSBI&gK_^7&a3GAS?R8&$YW-zjmo> zqs2J9zm{@@oyCD^v&&L4Nm0)^8criNa|S7q{~|6$&B$_0*<#&w#Uh=6jgqH0#9k`n zK#R_>2w)AH%RF!K3Bq_xV;-Z44Jpr4lhfk;*!tZaH>N83wCZi?G8mBl@SIAmJjyyU znD26aa8SXS+Cvj;z*i!iD>Cp;%F?&wvZ{3q&3ep!ttLY1sQQ$t*nga^Hq)yo@KfHk z0xsNgS5nppJK84t9{(_bc^qKaMXA5D+Jnqi2VPpUDBj(GOiJ{nxvYCaIhxeQ~>nO?F>Nzc>nK4mzc8?f( z>_fqIWDKE|FdN@H%2PINN`XJRdO8<2Wk=}3(NYIv1ycnsby1UVYu&%bXpiEL6mg`k z0q6tRX0o8ipN&gvMDfBCY&#_gTm`a$$)_^YpF|w$QPqq|b0Zabm2)MckX6;2=4Hap z+s%gwDqslpCeQM60PGj7Ym2@llI_ICor^IRz$4<2sp;vg6(KTj-e3&}Yi4(@yrSNI|ysmVy(g#%UXdiK`Lfbz!kMZMCeUy#JW9evEaO54n zNe0DyTwbNnn6@{Tj~S{>(!zzyir~f`U@SZ4{9dgV%US-z#}$E5 z`~TmQ#x7taF9n^eR0HyctuqHgT@+UK!tC&hj6yY+p)rs%)4Y!D_+Z~XmsTu6G8#NY zKr#y*WTuj!+S+w~IX1R9G<3O^FBZ4m{ee9~fKTSF24k${xeD$Zs95NNmCdD$hcdpy z>pXYDJu`&xH__P{IA~m{Vob2GLGd6;FdEN5)Do>O87GtVafDuJ2&<157P4<_eYop8 zIquD)dhUAK%0xr~<-q@&@n0s2$?)JLUWLDOd51CowbR^RQVI>!mPjvs53)6kqS#bZ z3Z_@lBp$I}0*vjr`FLbOiWp=-{1>{_JW<3VXrV3rRuo?ZweY#JEyzmriIu_FJbN(Q z$U=5KF?|FKwNXZg-5Qv-QAXQIF|cYP%Mg$DLfTt6l|b74%Wo}%6N{%-^sZm47hGk} z7ykDV%bMN6PzsWCB1*kp!2-gY)P9pVMma(NJBU0SM{o_q=RzN9oYz#98Ut3wVI8)w z&DtJBNg$aLe@^(!#>NKYe`>IGe`eK77gp#Kg6?25MxTZ_@6P$nq;bhAava#>U!+=4 z@FA-g|BL&0X+kRTXAx zjC%5{ikhd{L)1hs(g}mN3X(&f=2dM!MXL2X$nE~grffpv&rlSMb2ri3Uz^GlItG>= zV|HCj%x(K}yho2kQlXb~S@AMW>?UX$On^OtgQe=&*nR;3#o$DBZEk8%TmX$jnr`{8 z(A8Y2qz!4!2CODZ@3aRSQB7#^A&bP?B+%w_?V|g{Gf#_!ynO}me)EeOPs;m>tGB#% z>Rtw!;Z=Hv&I`JNKtKM`cvo^=dH8m=o@RaGNxplI(NX}I+^J96V{HKQRP(YLBNK)Ue z`WKh`9r;aOc3PR2eglw)5zvAtuck0N8h>5ss;*Nl_m0!eX2B%(FO4L`7Jl2DW+TW}O;&SY*2(Wlb${h1fR6qVx`4-dKcNtAx_+H0CP zWEIY);}*EX&{#r?Z>E4LaH<4bPE*7&a4y5Br{2uQ7%yEQ=v>x3xpb-Q|M#W-s?;2J zO7?Ca_ijHr5hPALKh28#m&$Y4Em?PC$D+4g)AG+}0)_Gg$Pk>B3F@Sg7$)O|K z<43xu!g(Qr5TxOh^DYnM*rbO5s0a20pVj8&eX#ja6QG`1DZOa=k_P%iv5fCDOS)rn zt$LLy?;P0lMOs^9Tiup!gsxn1)qn?Owq^+-c51E3LkBO8TWoJ#5syKVppxoy#;HxI z^0xct*S$CH;Imq25LW!aXW@tz$ zX+82^WGAZP(6TwcDE9CdVjKH`FO#Wm-&}<&VGBBm`YLn!5%wa5v&kY|*a^Q>fJze{B>%W;Woc%sb{J2kiVGSm&zN_PhwhA9XDu@pn_d_ zo;#eu&#VvCNixjuH_7A4a>u=S`zNMp!~_Yy{P4HI#y(f10BowOqOlm|9xBRou@kez zDVmX8&~NsfkLS|W^Jt?j^pHqfP{m49?4dJ{(KmX;=4owRnzBF%W0H@Yq-C+$gQIgJ zGRY2(rQ;FHeU9q70pcO?At}dS5)Rr-j^C;MYqiecld}MHCrzZvVvi_sx0iB#cwTv> zs1&S8h$hO)Jnm#C2Ljrxi}Tb7y=Xf%I`5HE3o#f-CUB(D@F_YnB&#n9GpxFv%)Xq4 zN@hY{t`gRf2C(v-((=8I+0Dgszci8etd4^{aFfeq-iMV*AF-qr)yjQ#PIjpcKB?4` zkhNqsuCtM2ecdcopZfY%lZ2aCtFmP7d+u#c<(_t@2YB`1znV{NKjq7D?L@SCuRPg` ze)L=gauHD36&NLM+=*Vq4_%@|ThACFA#&Nhd>5#kvKWA)T$CN| z5y-^E;@Z(#*a-A>{h68S^{VitCEzXwj?d1h4H`p*0Z9Jl^okQIc-d8H{U}joHTQGk>L8#D>gL8rZ&e8pw`4a{e?cLV1+-Dvd z=W3}>WmPc0_U{Qfk_W2mg+T3oHQyx^u@d7lIky4{NQGC5wKmjC@!nWpo_^bNCmK5s z$o>-`W`J|t^**odccrZtv;+_0U%zxYUJJctu70o@ss8im(630sja~$flUeC1CW^Ju zj7B0ah<|T4f-~SjG5kHrYhMK6Vu|T0)Btrr>_V98wgfYVWq(wl7=sqT(MU&XNPb}) zI2poDoKGV=De?u;X{(Tj9y!3VG(20BFITV2<~!)IyE6Jto%^DxVuEGY!(dHv*@#T9 zT`tzp3+i9)KqIGt1JEQIP1|1hmH`-P5Yd3cXPQ2UJmpThoDw7VI+Tna(UA$}Rs|c- zM^m3bhagMo8HMK3WagTm>ozrzW-iykN@JyXa$ZqlT%RB04t8@yFViA+-yRw!MNx7Hf^8eVEPa=R2Je0Tp-PJ%Pkw+Sw+U-stcro-2cCU*Flb`_QsO_>;Vb!z^2htSZurgWG84Ls zh(q55D+=$U$bR%qY}>JI&cQ<2A4UmMw602h+XC=&(j{1A1Ay?p&3W6XwR;h(f^i{b z;&1(6`CQzil6%}y`*poAtirl-Lixn6G7Kryur;L;Re48Flo~YPiD#BKd=?xES?>9$ z+I!|z5v3J+^3$^#8b_b1M6{Ups`Q_5Z0ddq6WMO|&rRHh8vb4om9aU+=%(KK7l6%Q zY7nB^Xl4R1l5*9}s${ibuU+n$DTX=BImrYm6DJT1Iq|xwFg62*8$a(8V4+#viQ;C`J!aBQ{wRZo)?qkGbtKOH z)PUnPEq+X?VPR>lC#jt@6smO-o&mYajUjlH~Gblz*N zr++QMShDlKZU?F?9_di_u9t~EW>88pkLpJoX@Pg<0L}mIl;OQjTpAxUty&EB)vz)+RqXrD&ac%KV%C`W!2(1iATzov0yLWkgVw^G1{4?_Q18>U|4 zW((OA)FU?${9%}SZq~X8>G+a`fQDIxt~DNcD!}uln38M|iz89b#BW|{m7Zz?Kq zLKUSbBIc6FIam<508*!>=TVf`vR;msy(+ia7E){A#DocaG1@HWoZbz~2Ov@1(!DmL zds;0#WMRTqZjyztUypr79kp?^_K&*t<8&`8M_~#8xCRf;V1!T+YwWXSN{&(Q8MY5Uxq#6qU{NC6xJB_kDB9JIuBlNQ z>GMBmkTKNPy6WGztA4*uSwNXhkmJ&}1MNKA`a%RbYXowa16N+_8mN{09w&h=5b1yq zA4%_MaX;SATF3_mPe;%87YXn5|@m(F}g5;8M6UQpj&N zGOhk!Ux0{2xQ{ug;-g)L=PW`ykhRg>W@yJkBG|r`afTC^*>U)b9qpyS5Gq?@dqa|} zqjIk&fwJ&@feM-*dAIL@1g7fO9wAt3EV5Vg-%$0glTOHj{|7Ii1#d;(NouZg6NeN2 zB;!(+sG4gvyf&dW@AiB@8puSR^S~8l!4daoN_c)OgYP01Xy@_V8hq6BVQ}lG^S8&LRlw{H58(Y}<)P)E|dZw*vsSourrQ{9b5(%WM_LodV1&ooL@Z6{RT5}*AU zJ!V0+9~+iSDVR&NPQ%OR5OZi4!ffS)LBvZ66~ek$I)BoCI7tc`eEIzkkA4fbsL_GSn_R;egIhOeS2pM>v4~|E+(G~5?fwCvSJc#dp+pP0Kbc=n2NW1G_SJBooz)$~P;RiA87y|dpR@%BK5zqoBE6VxeLE!F*>v;k_@ zL~LCuk$kE^pZ8!ZlU$h<(8L7$y*Sb|^64n10E=bcHg9x(nMm1CilEAaAXg}yMA+8g zDvXqX!hNDn)^~>ek7aG&LK%QJYHg1@;x8U8On z@5Bq$cJ0AGGP3Pnx$Ngk#w5!dj_MstlvRH+M}majIxd#eB(;d+x?DvW%wT-OFkhhj zo?bqnXC9>*HKUMNn#q9IVPlB5!JI1KqLbB{rk&ur37kjJ-}$=7&6QkKl_eOWa@Y6{ zFX>okkOlMO_y}KiP02!`9-5-nED%W-cVIZ>BZJPPNdXV6ZogES;=iuk8oWB$hR8>0 z%T65ztugW)>N<{K`Aqmc|Ie zF5*%)_9Z)o^U(NVZhZ|S8=M6Nb-S<>F^k;R|9HTOz#rZ%WYBCE50LOz99%Y?ZfzNB zB}t|a!O{w+DSSTCFUN4=sbu`n_XA;RKK9eOwO!F6nC{dmRjBh(i`_Y^>Pi!zHVhq& z44UdKUZ%uXWls&g4u6>O9x78YwzNs8f|lK*OUmk7%3EUi3QX?K6JP~?Q^)93?lLPa zUE20~f>)BW_acC43H1*bBAdXlz0eeKUX>7lC>S(g z|Livt+a6JjE*hC=J7sfXf$pcQZa1i!S9t$SUobWGkw&Mxq*(@@|8swe8^`dr#a1lX zSQa5PKj`Kb{loKP6Ws;-`p>Jq*w~{B@b7N(sHCjx*3?hhtu07VeMKH`IavxV{&0w` zGlPf9@dEt#5QXK*ti#atzDoJZe_Hj>oRgzZndK5now5;SbDAxKvt#IqN^X#e|B8no zSE^wNrdH)vC|b3CDE0iBZ4eHzX3Gh_^U>t2mLQAJDHs0#(kJ#{a@m0_eBB>)@l`YV zNFn>SHV%nrRoZ?n)vpKTM!K^WgneJ|#>o^}w8{hl

Esad3cKLhhg%=w%h*zVc*~Zz0MGgKD4oE(Gi2E) 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

0)BUrs@w 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 C5(xGH literal 0 HcmV?d00001 diff --git a/backup/img/menu1.png b/backup/img/menu1.png new file mode 100644 index 0000000000000000000000000000000000000000..b056be91c3452e64e8e69909f33092e58ae0526d GIT binary patch literal 208287 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

m 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[itj69-!1?=NmF6T9d;L73?afWO<7N@hY9Bf) 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 JSN6(0@IU#Oyr%#F literal 0 HcmV?d00001 diff --git a/backup/img/menu2.png b/backup/img/menu2.png new file mode 100644 index 0000000000000000000000000000000000000000..888cbf2547bb396a203e7f293b60c27b16e1091d GIT binary patch literal 158626 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­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

    }L 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 z%0A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 literal 0 HcmV?d00001 diff --git a/backup/img/shop4.png b/backup/img/shop4.png new file mode 100644 index 0000000000000000000000000000000000000000..fe595486ee697859727ec4f2b6799d1bea972a6e GIT binary patch literal 211910 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

    aP7i7BeW$Oxuv;4s;O02WcX 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 z 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

    x!JRFF$Y00SZU_J^`d#t*7U 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 z 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 literal 0 HcmV?d00001 diff --git a/backup/img/shop7.png b/backup/img/shop7.png new file mode 100644 index 0000000000000000000000000000000000000000..30a9e8d29f1c17026fb396dcfe8b1ec89b7e4245 GIT binary patch literal 111967 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%0D2_m^%geWKJO@fjc-e2k=j43W* zjsNX>IO3JR&;=`r+ngWQRtH-*f`7UJRa$mZ`4fdv4?Y#BYP_|A{%M(G$$-=*I43k1?Wa z%Qd4HoDulw#ReCG*{;2d^-jo(SO`++VEH-tPSuM3oXzqj-)fU|%@*KV3Cm72m(`x} zevatBCuGkR&~5oCzzIRwntypc5S@GSMom0NQ`c-u%9NH|} zP@EJl!x#QjvsFRKVzwxTGFWN1-s*XF-?^YLLiB$oLNBfiU!cDss8lj?BImQX2+%)o zAyP330~x=wguK7XT^5Eo?CD}2tZ1Y#b`kY2T8tnO7x6PBRlwHhfseI z|3S$qSSZ7|F9LiPahZh>G5sH7k%^`X;twmQEfSST#Kj3^P=a1yldwuBUNl)i3swq_ zQ&wEt%!C&1A1}A*Z9ae%`?{PRInFiCBGC)huj#gw=rlZf(PsXURux4Ipx$$`U=ULa zDjy9i4*gcy(2Vv4rEx{$iwAfqhgcp4k(bgJ4tAGP1eC|c~U(9q^bB8*gJ-QYVpLU*>38vhbRYYMZVjLLo`5oKl$*t@v`djN;)s zbe{Err`=dw=uU%0fS+2Q0rvjpli*)I z7$*%P#Xx7wOl_T;*gREC)%%l7hwHq}BA0wk<7^w#0foN)Qp%^*MB4I8`_+V7d*7H4Ckga(1F8SUa?r4%$GnRw=-CVUa4X0c^_ zt2h&a85K~pg! z9)rCCgX%hccqaVgb^|8um}ivLoRW!<9N(#_aDtAr?*kgHo`&zLU4q7udSAvUiPI}u ze@ohUn!TvlG@Fy$^tRWtn#We3D1VXoDsIDp4RNR}^#gJb!vYhsw4A{DquDR0r=>DbRc4+vA z`2ZNaL+#F5@LTjT;!0176cTLU!yaIX#wMTTkeAM*?MF0huABhS#mRztg97Ly^5bqV zVNBTaWZ|CnCr0^f)`IyTL{E}c7cjJ2f)Wg`Z>TcVCQ_}rJlqDd^)Mgi8ftpeoc2Q( ztF{jv0KXP9<(lf{3Y`d&LO-y~VmkQ@EVI}=L?sTP7*Wot3y!RQ4vonIAM;0X#W)+p zfn$%W@E^)w!c$wLkj2}7w4+6!T|FyN&WDL(**H$dC6Kya{HPr0AQ^a?2sxbXN_5a= zb1S5rJ`J{K*Mq}3?$F;5Cdxc?OI;iXTYVWx{6g$T`eK1FA{ z$!!9)i0ZNm%$s(5a=+Y96T0eyrmoNMz-OdWld5{4W>st{q^r{< zvSKA}q8OKwxUU<6-ivJ?)|$DVnKg|`o?oonrHwGu-{R&Cn0fWNjw(;5!BfUd$)@>$ zeA_wtwbB}YTwp3Fzg$dOer~Tn|EK#~>koTa$XQOJU@ThF^sWXnYbQO?j)%R7I|l-X z5#nd9^-j7@&b6CMBdFSxF}9|*2k_bZednXmEa#PH%UOW1JcK;l9{PLl*>W`}U^rFn zJO=Il>J9h>N*%`2T!Pi6mm_27&evEI>DE!0o)2LLD++^-trb>uK8H*BpL}0rA1ZpD zxVoO2@4kM&(*okGxq6MHd|pgOy!C^PaEBp-;h}@8kJ_uH#yB!O$K4!N5KkSLf*b#r z1<(bb?j|_bYV>Gj-1PcWuvi<_qgZqIYumLHCns&^O*B3LUVp>y&_ngm8r;^H+>d!n z!_BJGoH_Y)Td%A&rr$o-y2Ajy-GR4rBReIic)#>$rf*S6u^@p?XRQB62C;e9)eFv0 z?u8g9>q$0=Cn;OCQ|ozKz=yH0{((pLzn%Orj72xqh0^{EiB+Ww=bMA5&f74?JUqHI zbk_%Wk?V9|G?4-TQn3SeZ_@A1I4IY0u}Q)f@1okR(0~X!&v!`qjI`lC?C%H_aTSZR z%~_HJ&$mHi>rp69!g+$v01(vcX%`BOiULl3@G3Oap?y)JZLgMSl}(n#0F3y&eI(Nqk`~YGq~hbT47=H>$~}LDT2Otjk@FWzXJC=LyZulVZw}{v5B2w`=lB zO*nv*YCKcQy1RGl?bCi^1$S>waViPd1*wd?!(?XipM6lH6(iu6zg<`Z;kRV$CrB*i ziesgI{;r*zhyzTYciqQCm`&!$@Z?1^j8MoyQ=wj&=g3~C!{ub zzYAI2KzOli3An!R{3Q<2THT#XisJ``ER|#owEh;e*+6{p3QbPr0@PIjKiw{rm>dAo z;wzaX@`IDsYU7Yghi-Gi{&4q$IQeyCG&vVts_tXkYaaIjC3va8_(O`8Mkod4weP<( zR-ssXNJr*~K=}m(NPj6KKy?O-gJvj=&UI8iJiL#0IejV%H2Z{FLv5;F$gC%TC9Lyd zL=h5bv_?kVfRP`w=u^JQL?D-cMok+w13FzG@6HgWdQeIxMe;{^F?2LJ+%8BQ|KQJ2 z5uj7+o;|Rb+uzX0-j>Z}ex8M>^(FcH1kKvZ{@SG5KujLf6AI?vXaDi<>+(|ybz64Y zO)NcA3n#mnu0D06R>tpyK?|(lJu-Z`sk)Bnm#8qL@`&;LJ(q3!z9?gMp7k)JFuo`+1TdfFy#gSDiMpFj0Lay6Is0Fzq z{wxXHL*Tpj5B>Cz2kJt0f_0K^`*AkR{8ILiO%{_HheMH>wt}sJ$+a7^)tv#FJ>P-yTsP4#Uu|*N&}S(Osufowqv|tPB~6BbLf1pJPM7=p7OWJ9 zaUH;FX(GS<1w$7h0HS>l)_{iB0G!E?TilsNuXPmkzOY@cA8W&dyDi>&hpf;p<~BRP z!vKdPHC>sLS)sf+kU&%qmK|=+)nnGrww&I+!>6`)nFfap9cakq3jRhbh#mN|b=3I5pJX>|BSLk33Cv+Ae4)d3vI!dilIYLRZ5Vzf0VB$$~tR%vSdF(4}W5X z!RVHgxJpY+jL8ZrT$RJD_5=G6C(j5lOY&1PHPMgT0F~PyXU%EW*=aGRt3C3~`-OzI znvfzmmwRiVu3(5Lv5FPaPq#Zg+jl5kbFRW}E{qH}LrsTC-;4m}6GwEmPx9N%R;@ht zrZ<7}u^*@$078+AhsFTlBV%wW*Fecw)!yZ9{L$Ms(f|iBd{~qSEqMXaY#}|C8bVyW znUV=V74vaa%o_OzdxxQ- zU+g_@@J`Op5)_vuTX5DZw1mYHvU{l69q>_XxJAW^fa?V!V88!K@YNR}9oD1vcwpf- zVEgqqC3(dZoH;Px_ZRFi8o570$p)WRiOK)9k^id;`t1cO(4cbQv{ID&x*|XVKiK?#vM%j@gH}}FZTbFw$S)#^Oz*OH}c{yFWmmG75ct5Qahf4 z>|NQg4t9og*v`-V@itmKWNS9`;yL=)`RI)t^hGB<0W$Yd20l$`&1V46`3;Lv)5f;T zmC&cr&-J{RsN=V6O{6Dit{zuF#O^BsjiM+I`Bo~EZO$k2i8`X8k!Trf8zdFOFmfRe z-qO(K17qq@4O*FyMtn7BNGk+tfj^L6l+dljQIC|Zw^wyZ6~tx5sP-#noi$qpuEGS2 z|G+S?{f?{&Ofn3Te!IGu>~h|Y+sI~13J?X*guQ5^KOm7negzeK!x_73rSUMgp?v~< zgd}dq@p&}JwY;5yTq#MhuTq+7b1(F~Aoq#G0| z5fG$PK)M+rtuzv&VZ=rVla>uejq#4(yTA5%o^$Km)6co*o}#Y-Zy0#Kaq*T=ISi3j zxbY5IW(-x}{JXc)c8bYP_evGX1;8r*$$yO0FMgD++9cwbNM30Ke9+UGA>s%eV<$fF zw-b^K%pzh{G*oF^Et>65QGGC(-pF zDF=a|4_K_*BbArWQ*SS2jT*VTCQBLFaItKc5i}|l&=C&$Myh*1PIaML4y}ad%EiD)*i#-WswDnpcM1IOBcomjm_EXyouH zsNl1_7n`(RN;z2QdBEZzpGw9yp;cS0c;(=YQ~Tsqk6to}^` zGk?Q-&H}^y7G?zRyb*%<@JG5=Z~D({ri<;!%a#|V@1OMj5`O_H`WtA57z|pu&(Mm{ zP_|)!SeP~2MYFsQoZsdU%<8b_U)-npKI? z*h{ySNB?*;tp!=z9@)TUkLE*Oh|aVMglvQk^JwniZ8f>}M-NNB-(cBsr8NN*7*!AvC2~B1G=wJM zt_r$5?oEG351J%WNy;V}#Z$l=!lgAnBikCYFH`K19HVdmi1}NXjcwtae2-_kCe=?} zh3g1Jfh<$a&Be^?J*wY!Q*NePq~i@cZzmhNkv{6! zFJ?n)a;ZiUisuQL4CU7_R?_+OcD!O7a{ob2lT#CZg1#^TWEcJm7+<9F>+y;d{NUUF5!Z29%uBVmCwQxccinVlk4Eq49; z^v^p*Cr(FxpP_J2 z`sKN1|DtNJ_N!N)u#wJkXJPoI`Q;3aFVb)*6Bn5gcmv4!?DSgRW1shvZf=GM7lDx4 z>L5;@@>4XE?9OaFdc(jguaSk17U;`#UJMuWCj4;k(Sw(yzxs#AiGBsD$hZO3;63*X zvG}S4!Yvnw%#GgL!g@V!ixYj)>8NjysFPf^8~}`N2zheiMD7_qwW!zNB$+|249^ zDjJEX$#x0T{kOf%as92Ipum^d+&G9#%=jtg=@8l-0H~+Yz+Il4F#F9^9P`KAp^&Wn zUwu6by*dvkX=0;T1+f-&@+{)#7pyPhX?RVG_lDy|a@)ZyOxo0rLYn2nNz~=&`QN4Z zB?)6J(qUzQrQ@XMf=Bd_MWH( zlce9b-d-oRz-iRi?p5a%u2zhP--DHciRzt|Af`GU4K%G2YUDZo`#2-p@f^W>Cw5^bb^MkS; z*D*d%2e7`7d{F%F(>HmW0=b!=FHJ{M9ge!!!X{x{4BF@7ot+Td!tm9VbM&of$@Edr z^#RyZcJlV%1|Cw&nKTlj9Sadz&@&eE&;f-r^7UR!`nOxe!u(_kwAwX=TClrFa7d|N zo`w=3Ny3*$G^{ClrUO)ZBqXnF@;lfImW88aj_Fj<8)ho*G9sA$fZ2$PFR&0D>|>NW zq#S-V98Mm7dDskA3i%py3l%)wL#i@i7U30b-H}$l8tW{kN4>_qCv;cilyPw|%$9e= zzLK4A4g2*U{qz>To8o5$hkjQz-PZNLz=>Kj5m>~R|ztB}+c{6oPL0uS-osxFhoN`xQa&&6{j zIfZC=%0SN741AvV{>|vvnJg&F)Q-cyT{TaS^JveGI5>N1*?toIdoZMYU_1g&4fTLX+qk%A)7Ebm^vkQq z56h@bd>vzp;{-fF;+RGHtiT`EuIF88M?t%%UuDl1zS(+)(XUardSE^>%HFmj5fK}X zD=j`NPbz~0p8^2RzeP&5lZz`_o=hF?Nw|Pz%dNJhV-G@LG6oO4(B~4Kw1Oa{-m^XF zsO_Nf9z?LWznxpod8i#dN}|O1P3Z@Gn7&=*<wt5GAvs+G$zZbg$V* z({NLop$cA8w^mI6003yM41qv=1vfm92}LC+)k7tWz0|U#7nC~X0&`mI{%f2xfg(*<4r%rf%Q=_$H#eP3vSO<`6+Kg zrBW4zUU;W^$wVfd{$W)E@;zH#5QfxN2y#cjCK-l9<{K)PZY?P&Jb1*fmbwz|^@duC z9l=nnfusHxhPHHNjKW`OC^pdFeH`p>%*fRzCD`1W9vxWozes(2Vegb2>vatsiGh!v-Xo7S44?gT@tRaD#0Dm3&u(c$UGP`-9$3Y|(!OwK7spK_2X0~u^R-iT z9?y&R zrkveg{=nAPe#eHuq^oWxtt{W&Sj`Ue3eWeN!cgl!uu*J1GSYY5$&P)x8tWxThq)OS z4#BM+Ut(dQpokl~68N@pcr5RYb^F=n4$YbI?M4OWC_*Wd-m~VI9y3_OP2AJg*aP#O zl)iXk|Fi{H^!W^0e&2;W6iF-ttHRDSE>u>fAfFX5`KB4XHaIQ4oY?1p;8y%IrKD;B zTJbhffv{dk{y9pSDJy`@t= z%feFKic|m+8$73R%q|R=eR^+R#qe@sOJt?!T4!gwGr%8-jCTEm%sxck{L(q5yiAYS zg=73dJ%@FjNaQi>UbtnecNd)Z1mL`iwy9pi^2SwtB_+;|6&m{mf2$vTyrdXJAEx{ zVupxWlV{rel&I5lbP;-rBn!IC*^gl65<{&1cip-Yc0aMEA9%+TCa#JDszT?rnAa+* zCPcWDXn-BR53}9~#(zh{(Z?ZMqx%nZtdhf1RTziZpZ=vJ7u-=o|1WVa{dVCY)f-H!nPW6?V5LVSZ+ea8F=wa~t}z>ZxpwWRtg1++ zd#DxdTIHJ}ItKo=slB1jVOQpE=WsA_mt%t}7RjFkgBXeaV-Q}99qxK<;myKleCS8C zG3|-%H9D(C3teB(qqZH-tExCE{TAX3C#)S&D_BBwV33HQ0GMB(t`#@dPw!EOa%Yz_ zKMF3%Ygt^vN6J9<3!R5_E{{i*KXTm7=4G57D~GOJRUs+aM?@_O z$*&T(`XAo&a8;X^C9Q{t43{C=hcBVYT70j`Vw{U6-b)0J7`+2xW^&rY`4f9t8|>ZO z3n$P8?-JU@FOK~L_}>o`hGM#iiI3-ZQ>x0GT|Q^$z|m@MR`Wx8DY9=1fG?Js?qu;t z+uD{^ULljcRJo?()j&VAjh`F!BdNQ6{Ux4Rwi_sJV*0Qp%2&2U0l3zjt%hr24XiMz z^N+o`bO)AVl9MvNRLLQ zwVCL$yKN2T@fP>wooC7u-gyu-9zN+%X!j#8bcKsC({ZxsM0N{N(=yudnz3!~yGWa* zk-H^|ZBZhzq$C;VH30xMIqaXZz(UEiO6iSN#q6Xh8(!-cm(=|oB#=rNDlSe*6AIXm zHSQ5NhTIXv*Z9DbLH%6K!_PSt95eeKPh+FzGBo`Z%iBCF#h(ghJ{G*|bgweg@$H^( zXP#=RCd6}5B3e^P<|*r>BP716Rgdps1biv`5U1JeZ`NZI4>5nr;hDG_g!f@anDzH< zh(U%+48x8+JOtK_wGV=pUKd%)g4{lyhmPNv1wvdu9#7CsgAinYSd2+X6+)&Mgch7N z-X@$2F}M1QJw<9hA8w2zj8%vrzPV7(37J#+;hvQEVgm!M+$jj?r%T2_Z!I_Z?k1Th zrJwsuuT-9kN}wC$DMq_|g7Kf*KS+app-~t!>r4MmH1-Pz=Jxs$AW;}YpB|l^6)$Pt zjs0iMFZHt6$^VU}BBKVG0}jNxf8=98O%H6<=ODWrd@5nCTpaZalr1^(y!Hmk2mea6 z;iCV?Itc>m{6J6VlazQ)2qAZS;kqMGz3{LguWj1{|gE$tD1kM9#1z=rd_bbF0zx8I{nVg>*h|dn3a5=gy#IrQ%qZud%K?j zCV+e&d)p#4nU#d5D{6p)YE*W-hv z=u^xZfZN{zzb;J=J;iyykGb)2eK&}UxvCku@bu+e9D_t}=m_59fqQ!_U z$)cvNY4)q@GV6WpYVyy!{26!}3^56lc<^8K+4ToLS@b^SI^oAOwR=_(8x~_kJmjU^ zCuy&#QgU#$?}I1F&FoIwe!Qhl(pYp`1jnS_u};nXs!0iQf#|N+RIf-x`WWp96TXnR zvTE(5h$JZX(S!~^#sP6_J3J5^yWmTBIpN;6?}$-wr&=St$%eSEK0e z6n!4Ew!3?R*C=x{i#O-HztnYcSz6JQVxVVb>da5B-z-#$cMy2NLyRDcmB=g|H^Rf# zj~ANv3VgH92_qiRCZ4-VI3kBF6`V_)V9Ok4R4~Ywsi+IA}N}JBP*=st39C!z`ew}hiS^^p%&^xDgY$4^v*B!1N=-klmo$LpY zi2DiSy~}CvSv_nYdhhZl$75A-d^75fmevP+N+TL(50&)AbN_OmotLf{*zVy?JuM#N z_A#Hv0sK@h(yphcC7kV$tLHo;7E{|Bnta+}Zm)E`>WfAnOSdGA{mMJcl!5mmyJrw# zI@HZCN4}!nQ@;M;x(?`J+dVhKPlAPL<45#h$Q}fa7hcZ|Baz**=#0`l^h{gtjEN&T zz0ZnBSn_4H)s+RP=h{y;J+t?sl6Ys^BS4hzy0N9cbK@;YW~nzdvrwG9_+37XGju=6 z)PXwwMh@;=P8r~`M~Lsn<0r9~HrL2mnMy;s_uZIzr>y>C3I!EB*3{u86JF}t*OeXh%bhwywe-royYz%YFkNse9Wir{@Q%LVY)2QsV^oQoNt_% zdJw?$W9RjW^iKUvVnEtL&}eJ~8()8j$GyTi9)EY)>T0Z;Co=4CIuJ4=Zh86>B^TCn zyT&+)8Z-wP!gR+>p!MPXxk#i!#07p(kSPodX_DIc;*UPB6Ilp4HHZHm>lwye1oUQQ z*2|bT`=DbOJ*lCoC|$yRdDU)lSt+{FTnj?Uc- zK-vwY(m+<}VYo}w0oEm|WcxXU>vFGGX->gv2hL9&94cX1=~0P5+}k&FU$b*(=a!po z{9mcfR^QxrU27IuWXm1rccaE%m}is85y!S`pNuYistOw)o$vd4@f$=Udd(6$Q_Zw95ZwQ&W~=;pGvaSIB39;# z0o8PT?>Zz-;jK~ZdI%)^S`;4a2=Z5JPv$!D*hN+U%epVW4E^q?+{1EzvcdW5~?)RIQUuDmZ@ql=R2;D4PeP6@UwtkoQj1~!uEo#&BDWT zQ`X9%X`ACY%EStJWaut?SWT)(D zBOHR;Ww%(R8VC4&P{#QI!V|r64(W7RzMAC=AG{xA`r`*oxxmqL)&?X}W)Y8(o(RY8 zJPW@Tgjw^vic>V;Mhhh8AuaxWMj7A+s-^{%_7Ee0i_@}M&EEGW72 zUDr)szKzTCsr6)xYHoik(rHlyDF~JebN$eLCpSc*xM@wygNRbqtg=cfLO-NsL;PtB z#h;ipjXcL$CjhUm5j2}QjonjzGCk-s5iK?&*MuxiLF;0(H&dxUzu8v5gq@);rGbC! zx^slMwPfpPLz1aLuf>9^q<4@@%UBc;=3~x^`;V&o&`OW;6NP1E_F@NdMmlbfjl0bT ze~v4XUw>|qLSG2#(7}#-9%qSz3u!1l1ry>F4b&~nDo<+qGMMYM6@cT!_39wobph3 zc|nDQhxkQMPVZ6Bxa{Tb`L=xo9L!+8qGG-Sl74M;AkcPJxMcO=8&-?4OlPN0r!tA7 zLSaKVV*7Y!8-i_Pj6qMdQ3T~c;GsUdjl1g{_1OGUKly(vCzroUHG(c@+7f7I!r{vW z0V!fDq82|YPcQjyM)6U|vE$oZ*eJ$_{5AT5>6VS}IwYs}s+|#Z?g~4NF$(KESFtM0 zH16(&N>pox6K(4U=kwT5G2;WtIg^P=99Tt$Mw~D*SIP1A7B7VDeO)!;8#{3InSK%s zSF)6wzDTB)c$b-d-H#NoX%|WOJhY{$8GV`*^NVSlC5cJo-gVJtV47Ie4xtIIVl04` zCQrgojajdIXHZYYgeWEi+)m3>-1s=T4M-)BUy)hqIq|ujKl%|kUKr^K_%?-WqAgLy z>ldGr<@ykk<P?6O&lO6~GedJ8O z1fJhIfv^~ch~iU|UTic%ijR+vE7Rwh89mMZiAv6v101`TiP)~{o?NsNYxANkH?@0N zSyidU7{A8?9B>QJAx~&aS2x-o&|_20QWAe-y;b)(W(2nik>^^Ln>iklSwWp`G`UF< z`YiKmy-?LTC#z%!unN*rRDnlXQeYthM^8&HmG_&KdEd2iOE`V$m|r1D!G4o%r1%_f zJKM0u*`LKR$YcEXM+p>yq`3#2e5_r)BZ3_VBIiH4J6bS7t0_^4|FQCQF(7=bU7FR?@0@%$Fet(1w;>Dmn13* zhm;P_RGZ3@Ns?M;N?ZDmRMxutHRRxs5b3X|M{AGTXxtTZTvPQ@VOmeQs2a;$r6yx( zHGLQIZulh$Gj7%VlqmvwIn2gicTUYs@qQ1vD1WDPeDb>?lL|+mcs7>HFB$|tj+nYe zZ7RiQ@DTO=Q0bSM;NVNemQ7aW&DkJmkRJv?Y13!x zG0KVAQmQ~&i6#Pji4sB`v^igayv>PbL(q`Yx}a{2m8*n?-Zq4EP6Pw0FzgmTsQtHX z_yi5yq zn5y0h*P5hvKPPpAC|;2WmvHNr`0DDZe&!|8?v@gCDv-~wGB{-G7m~h;{(uAhIiu%S zNkg3!#uV5*o6YuHb8fdi(1AkA&MZ&MBxVP+O5&ih1zeNR-#lURtAaMac`e0iN_mG? zkw-om1$s;N`_0~JWldk^`+J-MBn*wgA_a?WDCiOhC!Jbd+Vu5MkY z7qi`-Y|nw-xe8&oj3%}`3w6zyCt9NYmo4ra8bN6ndMT(RLh#U{1m8NTg`!-6O1 zBUls!uL>U&*--ZnS@RC7c4wvH-&MSd-;}5Mdp{v9NWGB zNHsIrQHhK7<9;Iz9OuLpE3+=W*$V$9>WbvjnanSz*GynDZ`bL9pzuy**_%w{X8G;@ zutsw2CnwCBr))u{XXkz>2ox*UY<;tPAS230TrU&+J?x$}@FFDVyzcovjSTiZF=9Ie zbo+b8wpwb35_Xw7bH&%S)f#;*NsL_=_A)*FAU1Xr<uByjh?8c)wZ04RGUqN@Fg)Y`1OU< z{Y)#{YYEpIBbz`d%LZ7zmV413SdH>9KLTJbor$4%FQ{S(+XPd z!hjkB-Bfk0!aXS?u)Ayt76drCmYK*hJKo>Skh-D= zydNzdx_{@nd)2&ii64R5FrP3kQ=UnfUq%(t1scryJ*Ff5!hApK&Oyml-b+>b)1Eck z>Qb9KL1|h%HQD}iO&97F+EWk>UgNNGr%%mA^|S>SoZ{cI^T}XZ19rB#?p=Ss_fHyp zi+!K`zr|8kNjVhOhaYLw7SwM}UV~CH>${8orOG0YZ+)u$tKvSyqXT{=|KqmYC_e%LfiZT%_@w?1yX>55dO!~KeH@|c*0#>Nwl_Y&E$(NG!QZ0` z9ren%k2R}xlO*ECqC@Wo`JHg|(D2U05%rz4R1Be7p;wSuF+s&CrIU*d4U0Rs7|iB@ z$MSaFi?CWYvN+{GF;G`I4Bgg#<2IjoKfqqF`7F#esi2pb#-W*4L?Y{Uy%n)`=u5qC zL5IgPC+%H@#$N4*6o@~{FSDc@LuOS!BEW9sF?nQJgPJ-X(R|Lvi8?n{_-#BJ&awd{6==`V zl!fj87Tv!R^V=IqOLWfsHlfMQsQi7Fgo!(b5d4DFrZOMibEZ^3ItIro2Oa4YR<&bs zW`RuSTHoTO4@CCgd9a@*$`>T?kVQmsJ}<%Ex-LT!54b82F4>ID_mYCB78*} z&6FQ9`;$5=P;gKHZvr=zNWPoIa-{)~rM#+2yM>WORp~xeingpmN{aS# z=cT48v4w^=>^Qie1a?_OzBR+B7XIoY7D4$95%8n`?;z)yWV`!ci|}7Q$7qItVy9|B z*oOt7>a|0c=ktffhTGf(87@!A;j&)F=PE1MI+?R*%w;Rg=h5(VA(0J9L|v`pYK9-` zQRa3^eP5f5=54s?@ik}HhSF8*$Tj)QWYZqs{u!}&{c(BBx)s|BeMnYwcY*N@?<;!L z5!86c9$2f>A~igR9OxX;l$J%~a+D8k36*fNze7H9J_U6u=IB^Hs2b55;k_AxTZb5w zAN++Q9nt<2PT+ntNXHA@N~t_tb<(!X_c8CD#P)EnIm3=6DLwM;6r##RjT9eyQnZK) zV~Ri}cq`f-TA1=ehi)N}sZ7_#L)b}1?B};TV@QB06H!WNdYO?B@Ff-J?{1V`SZ&ek zUK}BemP4lkjM1EqhsKA}g@zQ+h^`vc6|DoqPlpc>`bV#r8GO=*EtDuWTsP`c?8xRB zN!iCuxgo$uf_ZnOFih>hJHa)dQ9~fY+RQ_8q>MsTS8jYeu8E$-(bD>her_<;S1w%^ z9xZB0l9c!+BbQA3WU9k(bZ(W5(=R!2YSLy6w@1z0VxD-p4A1?jlj)O}pS&zCV`8DX z$yHlcT~r)5)UI^W-v9@vzonsypZJ_I>!nLqB0ZHvgeRhQDAs2Z^sN!L`<4sc50SE1 z{&uVo8+b?PZ-6p8h~9rPw?ziaE1nsMch*`hlPNB?d0Gf=f=eWABL2kJ3H|mZe^>dK ziO(Gec_JEeNkJsXhonBv}d}k-&_YXhsI!volvz2@mYWW|txW;oj-q%2|i|E;{zEqR{ zY((-(Wv~aatdIvTQBdD<*71vX`i{HVgdU%b>%o%!ZDq%(M*$4hSlUVc`{)6lnlAs{ zr_gnNarP&T^9hoZyZld=Q`s2*2zk8~%S5QV(^D;MrIJ8tPBWi1x;pfP_Q-*crO&00 z+kgKrR4y+Tal-d!yXx18e_OSpT7?u1qYDcp<;r!Kl@%hL6=%2Vfhw+_UD1-0mGYsE z`rn==<$DC^2sherwVD40o4Dq;Op5z$eVh_(BY15inCUr`Lo9a3n+u=*(RHq@f#w(` z;s{{pniFSPrs@j`Ku%xOH>j(1*4wE&^_TcJc4CK{&&?xm_B;G)u4tjl0ww z#GPeO390$6myC_O4`7=dyDvG$1I?QPJpN-U6XJjhhGgy$b9a8tWzFS|*Ia9gU{nW> z7H>2ZwXAopdW@xYY||XBOubn1?=g5VDKbGR$^VD0=2gpV7+Iz3l=z{d)OM*h8lv3seV{uYutZ2k}+qmDEiaq@5!~tp_DkQi$9l-x5CxeNc zGImgJ=C{TPzmLBUO~&gWq*W-B-o!p5BPnf;#OpFvGW48U-fZ_Txr<=#{Th+54r*BP zLFbfjv3OK+8d1)$zYuH0-{7ci#?@YuZ;0#9Xp8rG4vh&nDjqcHna~6=HTs zna<1qd^GAR4)zmh?C;+Ade~{97CtSzI`xF?NhFfIeXB6t?xDrwg|ZFg!5meF_*WS=hezSQgBMaBroQIQ>?FX~ZXA~o3SZr3vv;?$de zc-iI^Ij$uWu`kEPD_deYASxfK=$oYKlld13b9`)$hPo|c5Rxsb1H8~(i$ZCOXjMMT zBGi7Mah;!w0wdXzgn5=fVrI{QD%#5y5$ZWF^D*^neqx@3;=?gryF(r8vkRuS^7uFL zE#s^4nlKMs4fpbuHJa1=$NALT?b`noDe_i-@7T<>yFEE&=V>+omNO2`DZkGCOOR_s z*H{;wSUVXVO7#`DKT&`CI&O*x}_X@v&eWuL0DQaMwJ@TYvaJm52MD literal 0 HcmV?d00001 diff --git a/backup/pom.xml b/backup/pom.xml new file mode 100644 index 0000000..9d5c185 --- /dev/null +++ b/backup/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + sdf1 + Sdf1_login + 1.0 + jar + + + 21 + UTF-8 + + + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + jitpack.io + https://jitpack.io + + + + + + io.papermc.paper + paper-api + 1.21.4-R0.1-SNAPSHOT + provided + + + + com.github.MilkBowl + VaultAPI + 1.7.1 + provided + + + org.xerial + sqlite-jdbc + 3.46.0.0 + provided + + + + + + Sdf1_login + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + + diff --git a/backup/src/main/java/Sdf1_login/AFKManager.java b/backup/src/main/java/Sdf1_login/AFKManager.java new file mode 100644 index 0000000..f714c1b --- /dev/null +++ b/backup/src/main/java/Sdf1_login/AFKManager.java @@ -0,0 +1,197 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +public class AFKManager { + + // 玩家最后活动时间(空闲才开始计时) + private final Map lastAction = + new HashMap<>(); + // 每个玩家当前的警告阶段 + private final Map lastStage = + new HashMap<>(); + // 是否已进入挂机检测(false=还在活动,不计时) + private final Map afkTracking = + new HashMap<>(); + + private final ConfigManager config; + private final JavaPlugin plugin; + + public AFKManager(ConfigManager config, + JavaPlugin plugin) { + this.config = config; + this.plugin = plugin; + } + + public void startCheck() { + new BukkitRunnable() { + public void run() { + tick(); + } + }.runTaskTimer(plugin, 20L, 20L); + } + + private void tick() { + if (!config.afkEnabled) return; + long now = System.currentTimeMillis(); + + for (Player p : Bukkit.getOnlinePlayers()) { + String name = p.getName(); + UUID uuid = p.getUniqueId(); + + if (!isLoggedIn(name)) continue; + if (config.getAfkWhitelist().contains(name)) + continue; + + // 确保有记录 + if (!lastAction.containsKey(uuid)) { + lastAction.put(uuid, now); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + long last = lastAction.get(uuid); + long idle = (now - last) / 1000; + int timeout = config.afkTimeout; + + // 还在活动范围内:不进入挂机检测 + if (idle < 60) { + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + // 超过60秒无活动:开始挂机检测 + afkTracking.put(uuid, true); + long remain = timeout - idle; + + // 已超时:踢出 + if (remain <= 0) { + p.kickPlayer("§c§l你已挂机 " + + fmtSec(idle) + ",被踢出"); + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + continue; + } + + int stage = lastStage.getOrDefault(uuid, 0); + + // 阶段3:最后10秒 + if (remain <= 10) { + if (stage < 3) { + lastStage.put(uuid, 3); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + remain + " 秒踢出", + 0, 22, 3); + playSound(p); + continue; + } + + // 阶段2:1分钟~10秒 + if (remain <= 60) { + if (stage < 2) { + lastStage.put(uuid, 2); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + fmtSec(remain) + " 踢出", + 5, 25, 5); + playSound(p); + continue; + } + + // 阶段1:3分钟提醒 + if (remain <= 180 && stage < 1) { + lastStage.put(uuid, 1); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§f还剩 " + fmtSec(remain) + " 踢出", + 10, 60, 20); + for (int i = 0; i < 3; i++) { + final int delay = i * 10; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + if (p.isOnline()) playSound(p); + }, delay); + } + } + + if (remain > 180) { + lastStage.remove(uuid); + } + } + } + + /** + * 玩家有活动时调用(移动/交互/放置/挖掘等) + * 重置计时,退出挂机检测 + */ + public void recordAction(UUID uuid) { + lastAction.put(uuid, System.currentTimeMillis()); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + } + + /** + * 玩家下线时调用 + * 彻底停止计时,清理所有数据 + */ + public void remove(UUID uuid) { + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + } + + /** + * 获取玩家当前是否在挂机检测中 + */ + public boolean isAfkTracking(UUID uuid) { + return afkTracking.getOrDefault(uuid, false); + } + + private boolean isLoggedIn(String name) { + return ((Main) plugin).getLoggedIn() + .contains(name); + } + + private void playSound(Player p) { + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + + private String fmtSec(long sec) { + if (sec < 60) return sec + "秒"; + long m = sec / 60; + long s = sec % 60; + return m + "分" + (s > 0 ? s + "秒" : ""); + } +} diff --git a/backup/src/main/java/Sdf1_login/AccountRequestManager.java b/backup/src/main/java/Sdf1_login/AccountRequestManager.java new file mode 100644 index 0000000..ae5eaad --- /dev/null +++ b/backup/src/main/java/Sdf1_login/AccountRequestManager.java @@ -0,0 +1,146 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class AccountRequestManager { + + public enum ApprovalMode { AUTO, MANUAL, HYBRID } + + public static class Request { + public final int id; + public final String requester; + public final String target; + public final String ip; + public final long time; + public String status; + public String denyReason; + + public Request(int id, String requester, String target, String ip) { + this.id = id; + this.requester = requester; + this.target = target; + this.ip = ip; + this.time = System.currentTimeMillis(); + this.status = "PENDING"; + this.denyReason = ""; + } + } + + private final Main plugin; + private final Map requests = new ConcurrentHashMap<>(); + private final Map pendingByPlayer = new ConcurrentHashMap<>(); + private int nextId = 1; + private ApprovalMode mode = ApprovalMode.MANUAL; + private int autoDelayMinutes = 60; + + public AccountRequestManager(Main plugin) { + this.plugin = plugin; + } + + public ApprovalMode getMode() { return mode; } + + public void setMode(String m) { + try { this.mode = ApprovalMode.valueOf(m.toUpperCase()); } + catch (Exception e) { this.mode = ApprovalMode.MANUAL; } + } + + public void setAutoDelayMinutes(int min) { + this.autoDelayMinutes = min; + } + + public Request createRequest(String requester, String target, String ip) { + int id = nextId++; + Request req = new Request(id, requester, target, ip); + requests.put(id, req); + pendingByPlayer.put(requester, id); + + if (mode == ApprovalMode.AUTO) { + scheduleAutoApprove(id); + } else if (mode == ApprovalMode.HYBRID) { + String targetIp = (String) plugin.getDb().getField(target, "ip_address"); + if (ip != null && ip.equals(targetIp)) { + scheduleAutoApprove(id); + } + } + return req; + } + + private void scheduleAutoApprove(int id) { + long delayTicks = (long) autoDelayMinutes * 60L * 20L; + Request req = requests.get(id); + if (req == null) return; + String requesterName = req.requester; + String targetName = req.target; + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 将在 " + autoDelayMinutes + " 分钟后自动通过"); + + Bukkit.getScheduler().runTaskLater(plugin, () -> { + Request r = requests.get(id); + if (r != null && "PENDING".equals(r.status)) { + approveRequest(id); + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 自动通过"); + Player requester = Bukkit.getPlayer(requesterName); + if (requester != null && requester.isOnline()) { + requester.sendMessage("§a[Sdf1_login] §f您的删除请求 #" + id + "(§e" + targetName + "§a)已自动通过!"); + } + } + }, delayTicks); + } + + public boolean approveRequest(int id) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "APPROVED"; + plugin.getDb().deleteUser(req.target); + pendingByPlayer.remove(req.requester); + // 清背包+传送出生点+踢出 + Player target = Bukkit.getPlayer(req.target); + if (target != null && target.isOnline()) { + target.getInventory().clear(); + target.getInventory().setArmorContents(new org.bukkit.inventory.ItemStack[4]); + target.getInventory().setExtraContents(new org.bukkit.inventory.ItemStack[1]); + target.teleport(target.getWorld().getSpawnLocation()); + target.kickPlayer("§c§l你的账号已被管理员删除,请重新注册"); + } + return true; + } + + + public boolean denyRequest(int id, String reason) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "DENIED"; + req.denyReason = reason; + pendingByPlayer.remove(req.requester); + return true; + } + + public List getPendingRequests() { + List list = new ArrayList<>(); + for (Request r : requests.values()) { + if ("PENDING".equals(r.status)) list.add(r); + } + return list; + } + + public Request getRequest(int id) { + return requests.get(id); + } + + public boolean hasPending(String player) { + return pendingByPlayer.containsKey(player); + } + + public void cancelPending(String player) { + Integer id = pendingByPlayer.remove(player); + if (id != null) { + Request r = requests.get(id); + if (r != null) r.status = "CANCELLED"; + } + } +} diff --git a/backup/src/main/java/Sdf1_login/AreaProtection.java b/backup/src/main/java/Sdf1_login/AreaProtection.java new file mode 100644 index 0000000..bc53d53 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/AreaProtection.java @@ -0,0 +1,4907 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import java.sql.*; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.GameMode; +import org.bukkit.event.EventPriority; + + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.entity.Item; + + + + +public class AreaProtection implements Listener { + + private final Main plugin; + public List giveEffects = new ArrayList<>(); + private final Map> playerAreas + = new HashMap<>(); + private final Map areas = + new ConcurrentHashMap<>(); + private Connection dbConnection; + + // 全局白名单(玩家) + private final Set globalPlayerWhitelist = + ConcurrentHashMap.newKeySet(); + // 区域白名单 key=区域名 value=玩家名集合 + private final Map> areaPlayerWhitelist = + new ConcurrentHashMap<>(); + // 全局黑名单(物品ID) + private final Set globalItemBlacklist = + ConcurrentHashMap.newKeySet(); + // 区域黑名单 + private final Map> areaItemBlacklist = + new ConcurrentHashMap<>(); + + // 选地 + private static final Material WAND = Material.BREEZE_ROD; + + private final Map pos1 = + new ConcurrentHashMap<>(); + private final Map pos2 = + new ConcurrentHashMap<>(); + private final Set selecting = + ConcurrentHashMap.newKeySet(); + public boolean denyRaid = false; + // ===== 统一文件夹 ===== + private final File rootDir; + private final File whitelistDir; + private final File globalWhitelistFile; + private final File oldDbFile; + private final File newDbFile; + + // 边框显示 + private final Map displayTaskIds = + new ConcurrentHashMap<>(); + private final Map> globalWhitelist + = new HashMap<>(); + private final Map> areaWhitelist + = new HashMap<>(); + private final Map protectedEntities + = new ConcurrentHashMap<>(); + // 区域给予的效果记录(贴标) + private final Map> playerAppliedEffects = new HashMap<>(); + // 延时清理任务 + private static final long MOVE_DEBOUNCE_MS = 100; + // 新:全部改为线程安全 + private final Map playerCurrentArea + = new ConcurrentHashMap<>(); + private final Map> playerMarkedEffects + = new ConcurrentHashMap<>(); + private final Map pendingClearTask + = new ConcurrentHashMap<>(); + private final Map pendingClearArea + = new ConcurrentHashMap<>(); + private final Map lastMoveProcess + = new ConcurrentHashMap<>(); + // 防止传送时重复处理 + private final Set teleporting + = ConcurrentHashMap.newKeySet(); + // 待清理的效果列表(与延时任务配合) + private final Map> pendingClearEffects + = new ConcurrentHashMap<>(); + + + +// ==================== 强制游戏模式 ==================== + + /** + * 监听玩家尝试切换游戏模式 + * 如果在强制游戏模式区域且不在豁免名单中,则取消切换 + */ + // 防止递归的标记 + private final Set modeSwitching + = ConcurrentHashMap.newKeySet(); + @EventHandler(priority = EventPriority.HIGHEST) + public void onGameModeChange( + PlayerGameModeChangeEvent event) { + if (event.isCancelled()) return; + + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + if (modeSwitching.contains(uid)) return; + + String areaName = playerCurrentArea.get(uid); + if (areaName == null) return; + + AreaConfig ac = areas.get(areaName); + if (ac == null) return; + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) return; + if (isExemptFromGameMode(p, areaName)) return; + + event.setCancelled(true); + + modeSwitching.add(uid); + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } finally { + modeSwitching.remove(uid); + } + + // ★ 切换后发一次中文提示 + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + ac.enforceGameMode + + " 模式,无法切换")); + } + + public Set getAlreadyForced() { + return alreadyForced; + } + + + // ==================== 定时强制游戏模式 ==================== + + private BukkitTask enforceTask; + + /** + * 启动定时强制任务 + * 每秒检查一次所有在线玩家 + */ + private BukkitTask enforceTask1; + + // 记录上次强制模式的玩家(避免重复发消息) + private final Set alreadyForced + = ConcurrentHashMap.newKeySet(); + + public void startEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + } + enforceTask = new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String areaName = + playerCurrentArea.get(uid); + if (areaName == null) { + // 不在任何区域,清除标记 + alreadyForced.remove(uid); + continue; + } + + AreaConfig ac = areas.get(areaName); + if (ac == null) continue; + if (ac.enforceGameMode == null + || ac.enforceGameMode + .isEmpty()) continue; + if (isExemptFromGameMode( + p, areaName)) { + alreadyForced.remove(uid); + continue; + } + + GameMode target = + GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + modeSwitching.add(uid); + try { + p.setGameMode(target); + } finally { + modeSwitching.remove(uid); + } + + // ★ 只在玩家已被强制过时才发消息 + // 首次不发,后续才提醒 + if (alreadyForced.contains(uid)) { + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + + ac.enforceGameMode + + " 模式,无法切换")); + } + alreadyForced.add(uid); + } else { + // 模式一致,清除标记 + alreadyForced.remove(uid); + } + } + } + }.runTaskTimer(plugin, 20L, 20L); + } + + public void stopEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + enforceTask = null; + } + } + + + + /** + * 检查玩家是否豁免于游戏模式强制 + * 豁免条件:全局白名单 / 区域白名单 / 模式排除名单 + */ + private boolean isExemptFromGameMode( + Player p, String areaName) { + String name = p.getName(); + + /* if (p.isOp()) { + plugin.getLogger().info( + "[防护-调试] 豁免原因: OP, 玩家=" + name); + return true; + }*/ + if (globalPlayerWhitelist.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 全局白名单, 玩家=" + + name);*/ + return true; + } + Set aw = + areaPlayerWhitelist.get(areaName); + if (aw != null && aw.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 区域白名单, 玩家=" + + name + " 区域=" + areaName);*/ + return true; + } + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 模式排除名单, 玩家=" + + name);*/ + return true; + } + /* plugin.getLogger().info( + "[防护-调试] 无豁免, 玩家=" + name);*/ + return false; + } + + /** + * 玩家进入区域时,如果该区域强制游戏模式, + * 则立即设置(排除豁免玩家) + */ + private void applyEnforcedGameMode(Player p, + String areaName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: ac=null, area=" + + areaName);*/ + return; + } + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: 未配置强制模式, area=" + + areaName);*/ + return; + } + + boolean exempt = isExemptFromGameMode( + p, areaName); + /* plugin.getLogger().info( + "[防护-调试] applyMode: 玩家=" + p.getName() + + " 区域=" + areaName + + " 强制=" + ac.enforceGameMode + + " 当前=" + p.getGameMode() + + " 豁免=" + exempt + + " OP=" + p.isOp() + + " 全白=" + globalPlayerWhitelist + .contains(p.getName()));*/ + + if (exempt) return; + + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + /* plugin.getLogger().info( + "[防护] 强制切换 " + p.getName() + + " → " + ac.enforceGameMode);*/ + } + } catch (Exception e) { + /* plugin.getLogger().warning( + "[防护] 强制模式失败: " + + e.getMessage());*/ + } + } + + public AreaProtection(Main plugin) { + this.plugin = plugin; + + // 统一根目录 + rootDir = new File(plugin.getDataFolder(), "区域防护"); + if (!rootDir.exists()) rootDir.mkdirs(); + + // 白名单子目录 + whitelistDir = new File(rootDir, "whitelists"); + if (!whitelistDir.exists()) whitelistDir.mkdirs(); + globalWhitelistFile = new File( + whitelistDir, "全局白名单.txt"); + + // 数据库迁移 + oldDbFile = new File( + plugin.getDataFolder(), "area_effects.db"); + newDbFile = new File(rootDir, "area_effects.db"); + migrateDatabase(); + + // 初始化 + initDatabase(); + writeDefaultConfig(); + loadAllAreas(); + loadWhitelists(); + recoverPendingEffects(); + } + public void recoverPendingEffects() { + try { + // 1. 遍历所有在线玩家(而不是遍历DB记录) + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 2. 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + // 3. 获取该玩家在DB中的所有历史记录 + List recordedAreas = + getPlayerAreas(uid); + + // 4. 清理不在当前区域的旧记录 + for (String recordedArea : recordedAreas) { + if (currentAc == null + || !currentAc.name + .equals(recordedArea)) { + // 玩家不在这个区域了 → 清DB + removePlayerEffects( + uid, recordedArea); + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在" + recordedArea + + ", 清理旧记录"); + } + } + + // 5. 如果在区域中 → 恢复/给予效果 + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } else { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在任何区域, 跳过"); + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 恢复效果失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + // 取消延时清理任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 清标记 + playerMarkedEffects.remove(uid); + playerCurrentArea.remove(uid); + } + + +// ==================== 数据库迁移 ==================== + + /** + * 如果旧位置有 db 文件,移到新位置 + * 如果新位置已有,不动 + * 如果旧位置也没有,在新位置创建 + */ + + private void migrateDatabase() { + if (newDbFile.exists()) { + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + oldDbFile.delete(); + } + return; + } + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + try { + java.nio.file.Files.move( + oldDbFile.toPath(), + newDbFile.toPath(), + java.nio.file.StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 迁移数据库失败: " + + e.getMessage()); + } + } + } + + + // 保存玩家进入区域时给予的效果 + private void savePlayerEffects(UUID uid, String playerName, + String areaName, List effects) { + try { + // 先删除该区域旧记录,避免重复 + PreparedStatement del = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + del.setString(1, uid.toString()); + del.setString(2, areaName); + del.executeUpdate(); + del.close(); + + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)"); + long now = System.currentTimeMillis(); + for (PotionEffectType type : effects) { + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, type.getName()); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, now); + ps.addBatch(); + } + ps.executeBatch(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 保存效果失败: " + e.getMessage()); + } + } + + // 查询玩家在某个区域获得的效果 + private List getPlayerEffects( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + PotionEffectType type = PotionEffectType + .getByName(rs.getString("effect_type")); + if (type != null) list.add(type); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + // 删除玩家在某个区域的效果记录 + private void removePlayerEffects(UUID uid, String areaName) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除效果失败: " + e.getMessage()); + } + } + + // 删除玩家所有效果记录 + private void removeAllPlayerEffects(UUID uid) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除全部效果失败: " + e.getMessage()); + } + } + + // 获取玩家所有记录的区域 + private List getPlayerAreas(UUID uid) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT DISTINCT area_name " + + "FROM player_effects WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("area_name")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询区域失败: " + e.getMessage()); + } + return list; + } + + // 查询所有待处理的效果(跨服重启也能恢复) + private List getAllPendingEffects() { + List list = new ArrayList<>(); + try { + Statement stmt = dbConnection.createStatement(); + ResultSet rs = stmt.executeQuery( + "SELECT uuid, player_name, area_name, " + + "effect_type FROM player_effects"); + while (rs.next()) { + list.add(new String[]{ + rs.getString("uuid"), + rs.getString("player_name"), + rs.getString("area_name"), + rs.getString("effect_type")}); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询全部效果失败: " + e.getMessage()); + } + return list; + } + + + private void writeDefaultConfig() { + File f = new File(rootDir, "末地保护区.txt"); + if (f.exists()) return; + + String endWorld = "world_the_end"; + for (org.bukkit.World w : Bukkit.getWorlds()) { + if (w.getEnvironment() + == org.bukkit.World.Environment.THE_END) { + endWorld = w.getName(); + break; + } + } + + final String ew = endWorld; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 末地保护区"); + pw.println("起点: " + ew + ",-30000000,-30000000"); + pw.println("终点: " + ew + ",30000000,30000000"); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("没收物品: BROWN_MUSHROOM,RED_MUSHROOM,MUSHROOM_STEW"); + pw.println("没收提示: 你携带了违禁品「蘑菇」,已被全部没收!"); + pw.println("通报批评: {player} 在末地违规携带蘑菇!"); + pw.println(); + pw.println("禁止放置方块"); + pw.println("禁止破坏方块"); + pw.println("禁止PVP"); + pw.println("禁止袭击"); + pw.println(); + pw.println("效果: 夜视 1 999"); + pw.println("进入提示: 末地保护区 - 请遵守规则"); + pw.println("离开提示: 已离开末地保护区"); + pw.close(); + plugin.getLogger().info("[防护] 生成末地配置, 世界: " + ew); + } catch (IOException ignored) { + } + } + + public void reload() { + loadAllAreas(); + loadWhitelists(); + // 强制输出醒目的中文日志 + plugin.getLogger().info("========================================"); + plugin.getLogger().info("[防护] 已重载 " + areas.size() + " 个区域"); + plugin.getLogger().info("[防护] 已重载白名单"); + plugin.getLogger().info("========================================"); + } + +// ==================== 消息格式化 ==================== + + /** + * 格式化区域消息 + * 1. & → §(颜色符号转换) + * 2.
    和 \n → 换行 + * 3. 内容 → §l内容§r(加粗) + * 4. 自动添加 [区域防护] 前缀 + */ + private String formatAreaMsg(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + String msg = raw; + + // & → § + for (int i = 0; i <= 9; i++) { + msg = msg.replace("&" + i, "§" + i); + } + for (char c : "abcdefklnor".toCharArray()) { + msg = msg.replace("&" + c, "§" + c); + } + + // 换行 + msg = msg.replace("
    ", "\n"); + msg = msg.replace("\\n", "\n"); + + // HTML → Minecraft格式(栈式解析,支持嵌套) + msg = convertHtmlTags(msg); + + // 前缀 + msg = "§c§l【区域防护】§r " + msg; + + return msg; + } + + /** + * 栈式HTML标签解析 + * 处理 text rest 这样的嵌套标签 + * 内层关闭时重新应用外层格式 + */ + private String convertHtmlTags(String msg) { + StringBuilder sb = new StringBuilder(); + java.util.Deque stack = + new ArrayDeque<>(); + int i = 0; + + while (i < msg.length()) { + if (msg.charAt(i) == '<' + && i + 1 < msg.length()) { + if (msg.charAt(i + 1) == '/') { + // 关闭标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 2, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0 && !stack.isEmpty() + && stack.peek() == code) { + stack.pop(); + if (!stack.isEmpty()) { + // 重置后重新应用外层格式 + sb.append("§r"); + for (char c : stack) { + sb.append("§").append(c); + } + } else { + sb.append("§r"); + } + } + i = end + 1; + } else { + // 开放标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 1, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0) { + stack.push(code); + sb.append("§").append(code); + } + i = end + 1; + } + } else { + sb.append(msg.charAt(i)); + i++; + } + } + return sb.toString(); + } + + private char htmlToMc(String tag) { + switch (tag) { + case "b": return 'l'; + case "i": return 'o'; + case "u": return 'n'; + case "s": return 'm'; + default: return 0; + } + } + +// =============== 加载 ==================== + + public void loadAllAreas() { + areas.clear(); + File[] files = rootDir.listFiles( + (File d, String n) -> n.endsWith(".txt")); + if (files == null) return; + for (File f : files) { + try { + String name = f.getName() + .replace(".txt", ""); + AreaConfig ac = parseArea(name, f); + areas.put(name, ac); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载失败: " + f.getName()); + } + } + } + + + private void initDatabase() { + try { + dbConnection = DriverManager.getConnection( + "jdbc:sqlite:" + newDbFile.getAbsolutePath()); + Statement stmt = dbConnection.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS player_effects (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "uuid TEXT NOT NULL," + + "player_name TEXT," + + "area_name TEXT NOT NULL," + + "effect_type TEXT NOT NULL," + + "effect_level INTEGER DEFAULT 0," + + "effect_duration INTEGER DEFAULT 999," + + "enter_time INTEGER NOT NULL)"); + stmt.executeUpdate( + "CREATE INDEX IF NOT EXISTS idx_uuid " + + "ON player_effects(uuid)"); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().severe( + "[防护] 数据库初始化失败: " + + e.getMessage()); + } + } + + +// ==================== 效果名称中英文映射 ==================== + + /** + * 将中文效果名转为 Bukkit 能识别的英文名 + */ + private String resolveEffectName(String name) { + if (name == null) return null; + String n = name.trim(); + // 如果已经是英文或命名空间格式,直接用 + if (n.contains(":") + || n.equals(n.toUpperCase())) { + return n; + } + // 中文 → 英文映射 + switch (n) { + case "伤害吸收": return "ABSORPTION"; + case "生命提升": return "HEALTH_BOOST"; + case "抗性提升": return "DAMAGE_RESISTANCE"; + case "抗火": return "FIRE_RESISTANCE"; + case "夜视": return "NIGHT_VISION"; + case "力量": return "INCREASE_DAMAGE"; + case "速度": return "SPEED"; + case "急迫": return "FAST_DIGGING"; + case "跳跃提升": return "JUMP"; + case "再生": return "REGENERATION"; + case "生命恢复": return "REGENERATION"; + case "治疗": return "HEAL"; + case "瞬间治疗": return "HEAL"; + case "伤害药水": return "HARM"; + case "瞬间伤害": return "HARM"; + case "虚弱": return "WEAKNESS"; + case "中毒": return "POISON"; + case "缓慢": return "SLOW"; + case "挖掘疲劳": return "SLOW_DIGGING"; + case "失明": return "BLINDNESS"; + case "隐身": return "INVISIBILITY"; + case "发光": return "GLOWING"; + // 村庄英雄 + case "村庄英雄": return "HERO_OF_THE_VILLAGE"; + case "幸运": return "LUCK"; + case "不幸": return "UNLUCK"; + case "水下呼吸": return "WATER_BREATHING"; + case "水下速掘": return "DOLPHINS_GRACE"; + case "缓降": return "LEVITATION"; + case "漂浮": return "LEVITATION"; + case "饱和": return "SATURATION"; + // 无效效果 + case "伤害": return "HARM"; + case "治疗药水": return "HEAL"; + default: + // 尝试直接用英文匹配 + return n; + } + } + + /** + * 全量容错:把全角字符统一转半角, + * 去掉零宽字符/不可见字符, + * 让用户怎么写都能正确解析。 + */ + private String normalizeLine(String input) { + if (input == null || input.isEmpty()) return ""; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + + // 跳过零宽字符和不可见字符 + if (c == '\u200B' // 零宽空格 + || c == '\u200C' // 零宽非连接 + || c == '\u200D' // 零宽连接 + || c == '\uFEFF' // BOM + || c == '\u00A0' // 不间断空格 + || c == '\u2028' // 行分隔符 + || c == '\u2029' // 段分隔符 + || c == '\u200E' // LTR标记 + || c == '\u200F' // RTL标记 + || c == '\u2060' // 字连接符 + || (c >= '\u2000' && c <= '\u200A')) { // 各种空格 + continue; + } + + // 全角→半角映射 + if (c >= '\uFF01' && c <= '\uFF5E') { + c = (char) (c - 0xFEE0); + } + + // 全角空格→半角空格 + if (c == '\u3000') { + c = ' '; + } + + sb.append(c); + } + + return sb.toString(); + } + + + private AreaConfig parseArea(String name, File f) + throws IOException { + AreaConfig ac = new AreaConfig(); + ac.name = name; + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean inBlockComment = false; + boolean inHtmlComment = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; +// ===== 全量容错预处理:全角→半角,去零宽字符 ===== + line = normalizeLine(line); + if (line.isEmpty()) continue; +// ===== 暴力调试:输出所有包含关键词的行 ===== + if (line.indexOf("游戏") >= 0 + || line.indexOf("模式") >= 0 + || line.indexOf("生存") >= 0 + || line.indexOf("创造") >= 0 + || line.indexOf("排除") >= 0 + || line.indexOf("豁免") >= 0 + || line.indexOf("强制") >= 0 + || line.indexOf("GameMode") >= 0 + || line.indexOf("SURVIVAL") >= 0 + || line.indexOf("CREATIVE") >= 0) { + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < line.length(); i++) { + hex.append(String.format("U+%04X ", (int) line.charAt(i))); + } + /* plugin.getLogger().info("[暴力调试] 行=[" + line + + "] 长度=" + line.length() + + " 字符码=" + hex.toString());*/ + } +// ===== 兜底扫描:确保和平白名单一定能解析 ===== + if (line.indexOf("白名单") >= 0 + || line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + int ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf('='); + if (ci < 0) ci = line.indexOf(' '); + + // 和平白名单 + if (line.indexOf("和平") >= 0 + && line.indexOf("白名单") >= 0 + && ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.peaceWhitelist.add(nm); + } + plugin.getLogger().info("[解析兜底] 和平白名单=" + + ac.peaceWhitelist); + } + + // 排除名单 + if (line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + if (ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.modeExempt.add(nm); + } + plugin.getLogger().info("[解析兜底] 排除名单=" + + ac.modeExempt); + } + } + } +// ===== 兜底结束 ===== + + if (inBlockComment) { + int endIdx = line.indexOf("*/"); + if (endIdx >= 0) { + inBlockComment = false; + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("/*")) { + int endIdx = line.indexOf("*/", 2); + if (endIdx >= 0) { + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + inBlockComment = true; + continue; + } + } + if (inHtmlComment) { + int endIdx = line.indexOf("-->"); + if (endIdx >= 0) { + inHtmlComment = false; + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("", 4); + if (endIdx >= 0) { + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + inHtmlComment = true; + continue; + } + } + if (line.startsWith("#") || line.startsWith("//")) { + continue; + } + int hashIdx = line.indexOf("#"); + if (hashIdx > 0) { + line = line.substring(0, hashIdx).trim(); + } + int slashIdx = line.indexOf("//"); + if (slashIdx > 0) { + line = line.substring(0, slashIdx).trim(); + } + if (line.isEmpty()) continue; + + if (line.startsWith("起点:")) { + String[] pp = line.substring("起点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x1 = Integer.parseInt(pp[1].trim()); + ac.z1 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("终点:")) { + String[] pp = line.substring("终点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x2 = Integer.parseInt(pp[1].trim()); + ac.z2 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("高度范围:")) { + String[] pp = line.substring("高度范围:".length()) + .trim().split("-"); + if (pp.length == 2) { + ac.yMin = Integer.parseInt(pp[0].trim()); + ac.yMax = Integer.parseInt(pp[1].trim()); + } + } else if (line.startsWith("没收物品:")) { + for (String s : line.substring("没收物品:".length()) + .trim().split("[,,]")) + ac.confiscateItems.add( + s.trim().toUpperCase()); + } else if (line.startsWith("禁止使用物品:")) { + for (String s : line.substring("禁止使用物品:".length()) + .trim().split("[,,]")) + ac.denyUseItems.add( + s.trim().toUpperCase()); + } else if (line.equals("禁止放置方块")) { + ac.denyBlockPlace = true; + } else if (line.equals("禁止破坏方块")) { + ac.denyBlockBreak = true; + } else if (line.equals("禁止PVP")) { + ac.denyPVP = true; + } else if (line.equals("禁止摔伤")) { + ac.denyFallDamage = true; + } else if (line.equals("禁止饥饿")) { + ac.denyHunger = true; + } else if (line.equals("禁止一切伤害")) { + ac.denyAllDamage = true; + } else if (line.equals("禁止丢弃物品")) { + ac.denyDrop = true; + } else if (line.equals("禁止末影珍珠")) { + ac.denyEnderPearl = true; + } else if (line.equals("禁止使用弓箭")) { + ac.denyBow = true; + } else if (line.equals("禁止骑乘")) { + ac.denyMount = true; + } else if (line.equals("禁止袭击")) { + ac.denyRaid = true; + } else if (line.equals("禁止药水")) { + ac.denyPotion = true; + } else if (line.equals("禁止爆炸")) { + ac.denyExplosion = true; + } else if (line.equals("禁止燃烧") + || line.equals("禁止火焰蔓延")) { + ac.denyFireSpread = true; + } else if (line.equals("清除所有效果") + || line.equals("清理所有效果")) { + ac.clearAllBadEffects = true; + } else if (line.startsWith("清除") + || line.startsWith("清理")) { + String effName = line.substring(2).trim(); + if (effName.endsWith("效果")) + effName = effName.substring(0, + effName.length() - 2); + if (!effName.isEmpty()) + ac.clearEffects.add(effName); + } else if (line.contains("和平模式") + && !line.contains("白名单") + && !line.contains("定时") + && !line.contains("开关")) { + ac.peaceMode = true; + plugin.getLogger().info("[解析] 和平模式已识别"); + + } else if (line.startsWith("效果")) { + String rest = line.substring(2).trim(); + // 去掉前导冒号、空格 + while (rest.length() > 0 + && (rest.charAt(0) == ':' + || rest.charAt(0) == ':' + || rest.charAt(0) == ' ' + || rest.charAt(0) == '\t')) { + rest = rest.substring(1); + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + // 第一个是效果名(可能是中文) + String effName = parts[0]; + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) {} + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) {} + } + + // ★ 用映射方法转为英文名 + String resolved = resolveEffectName(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → " + resolved + + " 等级=" + effLv + + " 时长=" + effDur); + + ac.giveEffects.add(new String[]{ + resolved, + String.valueOf(effLv), + String.valueOf(effDur)}); + // ★ 加日志确认解析结果 + PotionEffectType checkType = + resolveEffectType(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → type=" + + (checkType != null + ? checkType.getName() + : "NULL(失败)") + + " 等级=" + effLv + + " 时长=" + effDur); + + + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) { + } + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) { + } + } + ac.giveEffects.add(new String[]{ + parts[0], + String.valueOf(effLv), + String.valueOf(effDur)}); + } + } else if (line.startsWith("通报批评:")) { + ac.announceTemplate = line.substring( + "通报批评:".length()).trim(); + ac.enableAnnounce = true; + } else if (line.startsWith("没收提示:")) { + ac.confiscateMsg = line.substring( + "没收提示:".length()).trim(); + } else if (line.startsWith("进入提示:")) { + String raw = line.substring("进入提示:".length()).trim(); + ac.enterMsg = raw; + + // ★ 调试:打印原文和Unicode + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★进入提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("离开提示:")) { + String raw = line.substring("离开提示:".length()).trim(); + ac.leaveMsg = raw; + + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★离开提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("惩罚命令:")) { + ac.punishCommands.add(line.substring( + "惩罚命令:".length()).trim()); + } else if (line.startsWith("强制游戏模式:") + || line.startsWith("游戏模式:")) { + String prefix = line.startsWith("强制游戏模式:") + ? "强制游戏模式:" : "游戏模式:"; + String modeStr = line.substring(prefix.length()) + .trim().toUpperCase(); + // 容错:支持多种写法 + if (modeStr.equals("CREATIVE") || modeStr.equals("创造") + || modeStr.equals("1")) { + ac.enforceGameMode = "CREATIVE"; + } else if (modeStr.equals("SURVIVAL") || modeStr.equals("生存") + || modeStr.equals("0")) { + ac.enforceGameMode = "SURVIVAL"; + } else if (modeStr.equals("ADVENTURE") || modeStr.equals("冒险") + || modeStr.equals("2")) { + ac.enforceGameMode = "ADVENTURE"; + } else if (modeStr.equals("SPECTATOR") || modeStr.equals("旁观") + || modeStr.equals("spectator") + || modeStr.equals("3")) { + ac.enforceGameMode = "SPECTATOR"; + } + } else if (line.contains("排除名单") + || line.contains("模式排除") + || line.contains("模式豁免")) { + int colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf('='); + if (colonIdx >= 0) { + String rest = line.substring(colonIdx + 1).trim(); + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playerName = s.trim(); + if (!playerName.isEmpty()) { + ac.modeExempt.add(playerName); + } + } + } + + String rest = line.substring(/*idx*/ + 4).trim(); + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playername = s.trim(); + if (!name.isEmpty()) ac.modeExempt.add(name); + } + } + + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String wlName = s.trim(); + if (!wlName.isEmpty()) { + ac.peaceWhitelist.add(wlName); + } + } + } + + + + } else if (line.equals("禁止交互展示框") + || line.equals("禁止展示框")) { + ac.denyItemFrame = true; + + } + } + br.close(); + return ac; + } +// ===== 和平白名单操作 ===== + + // 添加生物名字到区域和平白名单 + public boolean addPeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.add(creatureName); + savePeaceWhitelist(ac); + return true; + } + + // 从区域和平白名单移除生物名字 + public boolean removePeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.remove(creatureName); + savePeaceWhitelist(ac); + return true; + } + +// ===== 模式排除名单操作 ===== + + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent e) { + Entity entity = e.getEntity(); + if (!isHostile(entity.getType().name())) return; + + Location loc = entity.getLocation(); + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + + if (ac == null || !ac.peaceMode) return; + + protectedEntities.put( + entity.getUniqueId(), + System.currentTimeMillis() + 5000); + + /* plugin.getLogger().info("[和平] " + + entity.getType().name() + + " 获得5秒保护期" + + " 区域=" + ac.name + + " 白名单=" + ac.peaceWhitelist + + " 白名单数量=" + ac.peaceWhitelist.size());*/ + } + + + public void cleanupProtectedEntities() { + long now = System.currentTimeMillis(); + int cleaned = 0; + Iterator> it = + protectedEntities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (now > entry.getValue()) { + it.remove(); + cleaned++; + } + } + if (cleaned > 0) { + /* plugin.getLogger().info("[和平模式] 清理过期保护: " + + cleaned + "个 剩余:" + + protectedEntities.size() + "个");*/ + } + } + + + public void banHostilesWithWhitelist(Player p, AreaConfig ac) { + long now = System.currentTimeMillis(); + // 看看附近到底有什么实体 + List nearby = p.getNearbyEntities(48, 48, 48); + /* plugin.getLogger().info("[和平] " + p.getName() + + " 附近实体总数=" + nearby.size());*/ + for (Entity ent : nearby) { + String typeName = ent.getType().name(); + /* plugin.getLogger().info("[和平] 实体: " + + typeName + " isHostile=" + isHostile(typeName));*/ + } + int scanned = 0; + int waiting = 0; + int saved = 0; + int banished = 0; + + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (!isHostile(ent.getType().name())) continue; + scanned++; + + UUID entUid = ent.getUniqueId(); + Long protectUntil = protectedEntities.get(entUid); + String customName = ent.getCustomName(); + + // 有保护期记录 + if (protectUntil != null) { + long remain = protectUntil - now; + + if (remain > 0) { + // 保护期内,精确剩余秒数 + int secondsLeft = (int) Math.ceil(remain / 1000.0); + waiting++; + + // 保护期内也检查名字(玩家可能刚贴上) + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + protectedEntities.remove(entUid); + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 白名单匹配,永久保留");*/ + continue; + } + + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 保护期剩余约" + secondsLeft + "秒");*/ + continue; + } + + // 保护期到了,最终检查 + protectedEntities.remove(entUid); + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + " 5秒到期");*/ + + // 到期时再查一次白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 到期但白名单匹配,保留");*/ + continue; + } + + // 没名字或不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名=[" + customName + "] 传送虚空");*/ + continue; + } + + // 没有保护期记录(非刷怪蛋生成的或记录被清了) + // 也检查一下白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 无保护期但白名单匹配,保留");*/ + continue; + } + + // 没有保护期且不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 无保护期,命名=[" + customName + "] 传送虚空");*/ + } + + /* plugin.getLogger().info("[和平] " + + " 扫描=" + scanned + + " 等待=" + waiting + + " 保留=" + saved + + " 传送=" + banished + + " 白名单=" + ac.peaceWhitelist);*/ + } + + private static final Set HOSTILE_TYPES = new HashSet<>(Arrays.asList( + "ZOMBIE", "ZOMBIE_VILLAGER", "HUSK", "DROWNED", + "SKELETON", "STRAY", "BOGGED", "WITHER_SKELETON", + "CREEPER", "SPIDER", "CAVE_SPIDER", "ENDERMITE", + "SILVERFISH", "BLAZE", "GHAST", "BIG_GHAST", + "MAGMA_CUBE", "SLIME", "WITCH", + "GUARDIAN", "ELDER_GUARDIAN", + "PILLAGER", "VINDICATOR", "EVOKER", "RAVAGER", + "ILLUSIONER", "VEX", + "PHANTOM", "SHULKER", "WARDEN", "BREEZE", + "PIGLIN", "PIGLIN_BRUTE", "ZOGLIN", + "ZOMBIFIED_PIGLIN", "ENDER_DRAGON" + )); + + private boolean isHostile(String typeName) { + return HOSTILE_TYPES.contains(typeName); + } + + // ==================== 白名单持久化 ==================== + + /** + * 保存白名单到文件 + * 路径:区域防护/whitelists/全局白名单.txt + * 路径:区域防护/whitelists/区域名.txt + */ + public void saveWhitelists() { + try { + // 1. 保存全局白名单 + PrintWriter pwGlobal = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(globalWhitelistFile), + StandardCharsets.UTF_8)); + for (String name : globalPlayerWhitelist) { + pwGlobal.println(name); + } + pwGlobal.close(); + + // 2. 删除旧的区域白名单文件(防止残留) + File[] oldFiles = whitelistDir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (oldFiles != null) { + for (File f : oldFiles) f.delete(); + } + + // 3. 保存各区域白名单 + for (Map.Entry> entry + : areaPlayerWhitelist.entrySet()) { + String name = entry.getKey(); + File areaFile = new File( + whitelistDir, name + ".txt"); + PrintWriter pwArea = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(areaFile), + StandardCharsets.UTF_8)); + for (String playerName : entry.getValue()) { + pwArea.println(playerName); + } + pwArea.close(); + } + + plugin.getLogger().info("[防护] 白名单已保存"); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 保存白名单失败: " + + e.getMessage()); + } + } + + + + // ==================== 区域检测 ==================== + + public AreaConfig getArea(String worldName, + int x, int y, int z) { + for (AreaConfig ac : areas.values()) { + if (ac.world.equalsIgnoreCase(worldName) + && inRect(x, z, ac) + && y >= ac.yMin + && y <= ac.yMax) { + return ac; + } + } + return null; + } + + private boolean inRect(int x, int z, AreaConfig ac) { + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + return x >= minX && x <= maxX + && z >= minZ && z <= maxZ; + } + + // ==================== 白名单检查 ==================== + + private boolean isPlayerWhitelisted(String player, + AreaConfig ac) { + if (globalPlayerWhitelist.contains( + player.toLowerCase())) + return true; + Set areaList = + areaPlayerWhitelist.get(ac.name); + return areaList != null + && areaList.contains(player.toLowerCase()); + } + + private boolean isItemBlacklisted(String itemId, + AreaConfig ac) { + if (globalItemBlacklist.contains( + itemId.toUpperCase())) + return true; + Set areaList = + areaItemBlacklist.get(ac.name); + return areaList != null + && areaList.contains(itemId.toUpperCase()); + } + + // ==================== 没收 ==================== + + private int confiscateItem(Player p, String itemTypeId) { + Material mat = Material.matchMaterial(itemTypeId); + if (mat == null) return 0; + ItemStack[] contents = p.getInventory().getContents(); + int removed = 0; + for (int i = 0; i < contents.length; i++) { + if (contents[i] != null + && contents[i].getType() == mat) { + removed += contents[i].getAmount(); + contents[i] = null; + } + } + p.getInventory().setContents(contents); + return removed; + } + + // ==================== 边框显示 ==================== + private void showBorder(Player p, AreaConfig ac, boolean temp) { + cancelBorder(p); + int taskId = Bukkit.getScheduler() + .runTaskTimer(plugin, new Runnable() { + @Override + public void run() { + if (!p.isOnline()) return; + Location loc = p.getLocation(); + int px = loc.getBlockX(); + int py = loc.getBlockY(); + int pz = loc.getBlockZ(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + for (int dy = 5; dy <= 30; dy += 5) { + for (int x = minX; x <= maxX; x += 4) { + for (int z = minZ; z <= maxZ; z += 4) { + if (x == minX || x == maxX + || z == minZ || z == maxZ) { + p.spawnParticle(Particle.END_ROD, + x + 0.5, py + dy, z + 0.5, + 1, 0, 0, 0, 0); + } + } + } + } + } + }, 0L, 20L).getTaskId(); + displayTaskIds.put(p.getUniqueId(), taskId); // taskId 是 int + if (temp) { + Bukkit.getScheduler().runTaskLater(plugin, + () -> cancelBorder(p), 300L); + } + } + + + private void cancelBorder(Player p) { + Integer taskId = displayTaskIds.remove(p.getUniqueId()); + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + } + + public void onPlayerQuit(String playerName) { + playerCurrentArea.remove(playerName); + } + + public void clearAllPlayerAreas() { + playerCurrentArea.clear(); + } + private void saveModeExempt(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("排除名单") + || trimmed.contains("模式排除") + || trimmed.contains("模式豁免")) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.modeExempt.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存模式排除名单失败: " + e.getMessage()); + } + } + + // ==================== 事件监听 ==================== + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + Location to = event.getTo(); + if (to == null) return; + + Location from = event.getFrom(); + if (from.getBlockX() == to.getBlockX() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + + // 防抖 + Long lastTime = lastMoveProcess.get(uid); + long now = System.currentTimeMillis(); + if (lastTime != null + && now - lastTime < MOVE_DEBOUNCE_MS) { + return; + } + lastMoveProcess.put(uid, now); + + // 检测区域 + String oldArea = playerCurrentArea.get(uid); + String newArea = null; + String forcedMode = null; + + for (Map.Entry entry + : areas.entrySet()) { + AreaConfig ac = entry.getValue(); + if (!ac.world.equals(to.getWorld().getName())) + continue; + int px = to.getBlockX(); + int pz = to.getBlockZ(); + int py = to.getBlockY(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + if (px >= minX && px <= maxX + && pz >= minZ && pz <= maxZ + && py >= ac.yMin && py <= ac.yMax) { + newArea = entry.getKey(); + forcedMode = ac.enforceGameMode; + break; + } + } + + /* plugin.getLogger().info( + "[防护-移动] " + p.getName() + + " 旧=[" + oldArea + + "] 新=[" + newArea + "]" + + " areas.size=" + areas.size() + + " world=" + to.getWorld().getName());*/ + + // ===== 2. 区域没变 ===== + if (Objects.equals(oldArea, newArea)) { + if (newArea != null) { + AreaConfig ac = areas.get(newArea); + if (ac != null) { + // ★ 每次移动都清除指定效果 + clearBadEffects(p, ac); + + // ★ 每次移动都强制游戏模式 + if (ac.enforceGameMode != null + && !ac.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + } + return; + } + + // 离开旧区域 + if (oldArea != null) { + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★开始离开处理: + + oldArea);*/ + + AreaConfig oldAc = areas.get(oldArea); + + // 离开提示 + if (oldAc != null && oldAc.leaveMsg != null + && !oldAc.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(oldAc.leaveMsg)); + } + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★收标: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + plugin.getLogger().info( + "[防护-移动-离开] 标记项: " + t.getName()); + } + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + plugin.getLogger().info( + "[防护-移动-离开] 取消旧延时"); + } + + // 创建延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = oldArea; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★创建延时任务: " + + toClear.size() + "个");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-移动-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); + + plugin.getLogger().info( + "[防护-移动-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size()); + + if (online == null || !online.isOnline()) { + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-移动-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-移动-清理] + cleared" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + } + } + + + // 进入新区域 + if (newArea != null) { + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + if (oldP != null) { + diffEffects(p, uid, oldP, newArea); + } + } + + AreaConfig newAc = areas.get(newArea); + if (newAc != null) { + if (newAc.enterMsg != null + && !newAc.enterMsg.isEmpty()) { + p.sendMessage( + formatAreaMsg(newAc.enterMsg)); + } + applyRegionEffects(p, uid, newArea, newAc); + // 清除效果 + clearBadEffects(p, newAc); + + } +// ===== 清除指定效果 ===== + if (newAc.clearEffects != null + && !newAc.clearEffects.isEmpty()) { + for (String effName : newAc.clearEffects) { + PotionEffectType type = + resolveEffectType(effName); + if (type != null && p.getPotionEffect(type) != null) { + p.removePotionEffect(type); + plugin.getLogger().info( + "[防护] ★清除效果: " + effName + + " 玩家=" + p.getName()); + } + } + } + +// ===== 清除所有效果 ===== + if (newAc.clearAllBadEffects) { + for (PotionEffect eff : p.getActivePotionEffects()) { + p.removePotionEffect(eff.getType()); + } + plugin.getLogger().info( + "[防护] ★清除所有效果, 玩家=" + p.getName()); + } + + if (forcedMode != null + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(forcedMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + if (newArea != null) { + playerCurrentArea.put(uid, newArea); + } else { + playerCurrentArea.remove(uid); + } + } +// ===== 清理袭击生物 ===== + + // 需要清理的袭击生物类型 + private static final Set RAID_MOBS = new HashSet<>(Arrays.asList( + "VEX", + "EVOKER", + "PILLAGER", + "VINDICATOR", + "WITCH", + "RAVAGER", + "PILLAGER" + )); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + Entity entity = event.getEntity(); + Location loc = entity.getLocation(); + + // 检查是否在防护区域内 + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + if (ac == null) return; + + // 禁止袭击:取消袭击事件 + if (ac.denyRaid) { + // 清理袭击生物 + String typeName = entity.getType().name(); + if (RAID_MOBS.contains(typeName)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] ★清理袭击生物: " + typeName + + " 区域=" + ac.name); + } + } + + // 没收物品检测 + if (ac.confiscateItems != null + && !ac.confiscateItems.isEmpty()) { + if (entity instanceof Item) { + Item item = (Item) entity; + String itemType = item.getItemStack() + .getType().name(); + if (ac.confiscateItems.contains(itemType)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] 没收违禁掉落物: " + + itemType); + } + } + } + } + + + /** + * 进入区域时发放效果 + * 流程:查DB → 有记录跳过写入 → 无记录写入DB + * → 给效果 → 贴标 + */ + private void applyRegionEffects(Player p, UUID uid, + String areaName, AreaConfig ac) { + /* plugin.getLogger().info( + "[防护-贴标] ★★★方法被调用! 玩家=" + + p.getName() + " 区域=" + areaName + + " ac=" + (ac != null) + + " 效果配置数=" + + (ac != null ? ac.giveEffects.size() : "null"));*/ + + if (ac == null || ac.giveEffects.isEmpty()) return; + if (ac.giveEffects.isEmpty()) return; + // 查DB:该玩家在此区域是否已有效果记录 + List dbEffects = + getPlayerEffectNames(uid, areaName); + + if (dbEffects.isEmpty()) { + // 无记录 → 写入DB + for (String[] eff : ac.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t == null) continue; + saveSingleEffect(uid, p.getName(), + areaName, t.getName()); + } + plugin.getLogger().info( + "[防护] DB写入, 玩家=" + p.getName() + + " 区域=" + areaName); + } else { + plugin.getLogger().info( + "[防护] DB已有记录, 跳过写入, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + + // 给效果 + 贴标 + List applied = new ArrayList<>(); + for (String[] eff : ac.giveEffects) { + try { + PotionEffectType type = + resolveEffectType(eff[0]); + if (type == null) continue; + int lv = Integer.parseInt(eff[1]) - 1; + int dur = Integer.parseInt(eff[2]) * 20; + + PotionEffect existing = + p.getPotionEffect(type); + if (existing != null + && existing.getAmplifier() >= lv) { + continue; + } + p.addPotionEffect( + new PotionEffect(type, dur, lv)); + applied.add(type); + } catch (Exception ignored) { + } + } + + // 贴标 + // 新代码(合并,不覆盖): + List existingMarks = + playerMarkedEffects.get(uid); + if (existingMarks != null && !existingMarks.isEmpty()) { + // 已有标记,合并新增的 + for (PotionEffectType t : applied) { + if (!existingMarks.contains(t)) { + existingMarks.add(t); + } + } + /* plugin.getLogger().info( + "[防护-贴标] 合并标记: " + + existingMarks.size() + + " 个, 玩家=" + p.getName());*/ + } else { + // 无标记,新建 + playerMarkedEffects.put(uid, applied); + /* plugin.getLogger().info( + "[防护-贴标] 新建标记: " + + applied.size() + + " 个, 玩家=" + p.getName());*/ + } + } + + /** + * 离开区域时,延时15秒清理效果 + * 流程:收标 → 15秒后清效果 → 确认清空 → 清DB + */ + private void scheduleEffectClear(Player p, UUID uid, + String areaName) { + // 收标 + List marked = + playerAppliedEffects.remove(uid); + + plugin.getLogger().info( + "[防护] 收标 " + (marked != null ? marked.size() : 0) + + " 个, 玩家=" + p.getName() + + " 区域=" + areaName); + + // 启动15秒延时 + pendingClearArea.put(uid, areaName); + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + + // 清除效果 + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + + // 确认清空后清DB + removePlayerEffects(uid, areaName); + plugin.getLogger().info( + "[防护] 15秒已到, 清除 " + + (marked != null ? marked.size() : 0) + + " 个效果 + DB, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + }.runTaskLater(plugin, 300L); // 15秒 = 300 tick + + pendingClearTask.put(uid, task); + } + public Set getAllAreaNames() { + return areas.keySet(); + } + + /** + * 15秒内进了新区域: + * 清除旧区域有、新区域没有的效果 + */ + private void diffEffects(Player p, UUID uid, + String oldAreaName, String newAreaName) { + AreaConfig oldAc = areas.get(oldAreaName); + AreaConfig newAc = areas.get(newAreaName); + if (oldAc == null || newAc == null) return; + + // ★ 从 pendingClearEffects 获取旧标记 + List oldMarked = + pendingClearEffects.remove(uid); + + if (oldMarked == null || oldMarked.isEmpty()) { + plugin.getLogger().info( + "[防护-跨区域] 无待清理效果"); + return; + } + + // 新区域的效果集合 + Set newTypes = new HashSet<>(); + if (newAc != null) { + for (String[] eff : newAc.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t != null) newTypes.add(t); + } + } + + int cleared = 0; + for (PotionEffectType type : oldMarked) { + if (!newTypes.contains(type)) { + p.removePotionEffect(type); + cleared++; + plugin.getLogger().info( + "[防护-跨区域] 清除: " + + type.getName()); + } else { + plugin.getLogger().info( + "[防护-跨区域] 保留: " + + type.getName() + + " (新区域也有)"); + } + } + + removePlayerEffects(uid, oldAreaName); + plugin.getLogger().info( + "[防护-跨区域] 完成: 清除" + + cleared + "/" + oldMarked.size() + + " 个, " + oldAreaName + + " → " + newAreaName); + } + +// ===== DB 辅助方法(简化版)===== + + /** + * 查询玩家在某个区域的效果记录(返回英文名列表) + */ + private List getPlayerEffectNames( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid=? AND area_name=?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("effect_type")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + /** + * 写入单条效果记录 + */ + private void saveSingleEffect(UUID uid, + String playerName, String areaName, + String effectType) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?,?,?,?,?,?,?)"); + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, effectType); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 写入效果失败: " + e.getMessage()); + } + } + + // ==================== 违禁品没收 ==================== + + /** + * 检查玩家是否豁免于没收 + * 全局白名单 / 区域白名单 / modeExempt / OP 都跳过 + */ + private boolean isExemptFromConfiscation( + Player p, String areaName) { + String name = p.getName(); + + if (globalPlayerWhitelist.contains(name)) + return true; + + Set areaWl = + areaPlayerWhitelist.get(areaName); + if (areaWl != null && areaWl.contains(name)) + return true; + + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) + return true; + + return false; + } + + /** + * 没收玩家背包中的违禁品 + */ + private void confiscateItems(Player p, + AreaConfig ac) { + boolean confiscated = false; + + for (int i = 0; + i < p.getInventory().getSize(); i++) { + ItemStack item = p.getInventory().getItem(i); + if (item == null) continue; + + String typeName = item.getType().name(); + if (ac.confiscateItems + .contains(typeName)) { + p.getInventory().setItem(i, null); + confiscated = true; + } + } + + if (confiscated) { + String msg = ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty() + ? ac.confiscateMsg + : "§c你的违禁品已被没收"; + p.sendMessage(msg); + + // 通报批评 + if (ac.enableAnnounce + && ac.announceTemplate != null) { + String text = ac.announceTemplate + .replace("{player}", + p.getName()); + Bukkit.broadcastMessage(text); + } + } + } + + + + private String getChineseGameMode(GameMode mode) { + switch (mode) { + case SURVIVAL: return "生存模式"; + case CREATIVE: return "创造模式"; + case ADVENTURE: return "冒险模式"; + case SPECTATOR: return "旁观模式"; + default: return mode.name(); + } + } + + private void handleEnter(Player p, AreaConfig ac) { + UUID uid = p.getUniqueId(); + + // 清除负面效果 + clearBadEffects(p, ac); + + // 给予效果 + List given = new ArrayList<>(); + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + given.add(type); + } + } + + removePlayerEffects(uid, ac.name); + if (!given.isEmpty()) { + // savePlayerEffects(uid, p.getName(), ac.name, given); + } + + // 白名单检查(最先执行) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 游戏模式切换(只对非白名单玩家) + if (ac.enforceGameMode != null) { + boolean exempt = isPlayerExemptFromModeChange(p, ac); + if (!exempt) { + GameMode target = GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + p.sendMessage("§e§l[区域防护] §f游戏模式已切换为: " + + getChineseGameMode(target)); + } + } + } + + // 和平模式/袭击检查 + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + handleConfiscate(p, ac); + } + + private boolean isPlayerExemptFromModeChange( + Player p, AreaConfig ac) { + // if (p.isOp()) return true; + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return true; + } + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return true; + } + if (ac.modeExempt.contains(p.getName())) { + return true; + } + return false; + } + + private void banRangedHostiles(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (isRangedHostile(ent.getType().name())) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + /** + * 按配置清除效果 + * denyRaid → 清除袭击相关 + * denyAllEffects → 清除所有负面+中性 + * denyAllEffects > denyRaid(全清优先) + */ + private void clearBadEffects(Player p, AreaConfig ac) { + // 和平模式 > 清除所有效果 > 禁止单个 + if (ac.peaceMode || ac.clearAllBadEffects) { + PotionEffectType[] allBad = { + PotionEffectType.SLOWNESS, + PotionEffectType.MINING_FATIGUE, + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.NAUSEA, + PotionEffectType.BLINDNESS, + PotionEffectType.HUNGER, + PotionEffectType.WEAKNESS, + PotionEffectType.POISON, + PotionEffectType.WITHER, + PotionEffectType.LEVITATION, + PotionEffectType.UNLUCK, + PotionEffectType.DARKNESS, + PotionEffectType.WIND_CHARGED, + PotionEffectType.WEAVING, + PotionEffectType.OOZING, + PotionEffectType.INFESTED, + PotionEffectType.BAD_OMEN, + PotionEffectType.TRIAL_OMEN, + PotionEffectType.RAID_OMEN + }; + for (PotionEffectType type : allBad) { + p.removePotionEffect(type); + } + return; + } + + // 禁止袭击 → 自动关联清除 + if (ac.denyRaid) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + p.removePotionEffect(PotionEffectType.RAID_OMEN); + } + + // 配置文件单个清除 + for (String effName : ac.clearEffects) { + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.removePotionEffect(type); + } + } + } + + + private PotionEffectType resolveEffectType(String name) { + if (name == null || name.isEmpty()) return null; + + // 去掉"效果"后缀 + String clean = name; + if (clean.endsWith("效果")) { + clean = clean.substring(0, clean.length() - 2); + } + + // 硬编码中文名→英文ID映射(不依赖任何外部Map) + switch (clean) { + // 负面 + case "缓慢": + return resolveEffectType("slowness"); + case "挖掘疲劳": + return resolveEffectType("mining_fatigue"); + case "瞬间伤害": + return resolveEffectType("instant_damage"); + case "反胃": + return resolveEffectType("nausea"); + case "失明": + return resolveEffectType("blindness"); + case "饥饿": + return resolveEffectType("hunger"); + case "虚弱": + return resolveEffectType("weakness"); + case "中毒": + return resolveEffectType("poison"); + case "凋零": + return resolveEffectType("wither"); + case "飘浮": + return resolveEffectType("levitation"); + case "霉运": + return resolveEffectType("unluck"); + case "黑暗": + return resolveEffectType("darkness"); + case "蓄风": + return resolveEffectType("wind_charged"); + case "盘丝": + return resolveEffectType("weaving"); + case "渗浆": + return resolveEffectType("oozing"); + case "寄生": + return resolveEffectType("infested"); + // 中性 + case "不祥之兆": + case "不祥征兆": + return resolveEffectType("bad_omen"); + case "袭击之兆": + case "袭击征兆": + return resolveEffectType("raid_omen"); + case "试炼之兆": + case "试炼征兆": + return resolveEffectType("trial_omen"); + // 正面 + case "迅捷": + return resolveEffectType("speed"); + case "急迫": + return resolveEffectType("haste"); + case "力量": + return resolveEffectType("strength"); + case "瞬间治疗": + return resolveEffectType("instant_health"); + case "跳跃提升": + return resolveEffectType("jump_boost"); + case "生命恢复": + return resolveEffectType("regeneration"); + case "抗性提升": + return resolveEffectType("resistance"); + case "抗火": + return resolveEffectType("fire_resistance"); + case "水下呼吸": + return resolveEffectType("water_breathing"); + case "隐身": + return resolveEffectType("invisibility"); + case "夜视": + return resolveEffectType("night_vision"); + case "发光": + return resolveEffectType("glowing"); + case "生命提升": + return resolveEffectType("health_boost"); + case "伤害吸收": + return resolveEffectType("absorption"); + case "饱和": + return resolveEffectType("saturation"); + case "幸运": + return resolveEffectType("luck"); + case "村庄英雄": + return resolveEffectType("hero_of_the_village"); + case "缓降": + return resolveEffectType("slow_falling"); + case "潮涌能量": + return resolveEffectType("conduit_power"); + case "海豚的恩惠": + return resolveEffectType("dolphins_grace"); + case "致命中毒": + return resolveEffectType("poison"); + default: + break; + } + + +// 兜底:直接用原版英文ID查(★ 注意是 PotionEffectType.getByName,不是 resolveEffectType) + PotionEffectType fallback = PotionEffectType.getByName(clean); + if (fallback != null) return fallback; + fallback = PotionEffectType.getByName(clean.toLowerCase()); + if (fallback != null) return fallback; + + plugin.getLogger().warning("[防护] 无法识别效果: " + name); + return null; + + } + + + private static final Map EFFECT_MAP + = new HashMap<>(); + + + private void clearRaidEffects(Player p) { + boolean cleared = false; + try { + if (p.hasPotionEffect(PotionEffectType.BAD_OMEN)) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + try { + if (p.hasPotionEffect(PotionEffectType.RAID_OMEN)) { + p.removePotionEffect(PotionEffectType.RAID_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + if (cleared) { + p.sendMessage("§c§l[区域防护] §f已清除袭击相关效果"); + } + } + + public Map> + getPendingClearEffects() { + return pendingClearEffects; + } + + public void onPlayerOffline(UUID uid, String playerName, + String areaName) { + // 清除该玩家在该区域的所有效果 + List given = getPlayerEffects(uid, areaName); + for (PotionEffectType type : given) { + // 只清除我们给予的效果 + Player p = Bukkit.getPlayer(uid); + if (p != null) { + p.removePotionEffect(type); + } + } + // 清除DB记录 + removePlayerEffects(uid, areaName); + plugin.getLogger().info("[防护] " + playerName + + " 下线,已清除" + given.size() + + "个效果并清理DB"); + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent e) { + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + if (from.getWorld() == to.getWorld() + && from.distance(to) < 5) return; + + Player p = e.getPlayer(); + UUID uid = p.getUniqueId(); + + // 标记传送中 + teleporting.add(uid); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + teleporting.remove(uid); + }, 5L); + + AreaConfig fromArea = getArea( + from.getWorld().getName(), + from.getBlockX(), from.getBlockY(), + from.getBlockZ()); + AreaConfig toArea = getArea( + to.getWorld().getName(), + to.getBlockX(), to.getBlockY(), + to.getBlockZ()); + + String fromName = fromArea != null + ? fromArea.name : null; + String toName = toArea != null + ? toArea.name : null; + + /* plugin.getLogger().info( + "[防护-传送] ★★★进入onTeleport: " + + p.getName() + + " [" + fromName + "] → [" + toName + "]" + + " teleporting=" + teleporting.contains(uid));*/ + + if (Objects.equals(fromName, toName)) { + /* plugin.getLogger().info( + "[防护-传送] 区域相同, 跳过");*/ + return; + } + + // ===== 离开旧区域 ===== + if (fromName != null) { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★开始离开处理: " + + fromName);*/ + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★收标结果: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + /* plugin.getLogger().info( + "[防护-传送-离开] 标记项: " + + t.getName());*/ + } + } else { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★标记为null! " + + "尝试其他key查找...");*/ + // 遍历查找可能的标记 + for (Map.Entry> entry + : playerMarkedEffects.entrySet()) { + if (entry.getKey().equals(uid)) { + /* plugin.getLogger().info( + "[防护-传送-离开] 找到标记! 效果数=" + + entry.getValue().size());*/ + } + } + /* plugin.getLogger().info( + "[防护-传送-离开] playerMarkedEffects大小=" + + playerMarkedEffects.size());*/ + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 创建15秒延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = fromName; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★创建延时任务: " + + toClear.size() + "个效果");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-传送-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); +/* + plugin.getLogger().info( + "[防护-传送-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size() + + " 区域=" + clearArea);*/ + + if (online == null + || !online.isOnline()) { + plugin.getLogger().info( + "[防护-传送-清理] 玩家不在线, 清DB"); + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-传送-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-传送-清理] ★★★完成: 清除" + + cleared + "/" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + + } + + // 离开提示 + if (fromArea != null && fromArea.leaveMsg != null + && !fromArea.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(fromArea.leaveMsg)); + } + } + + // ===== 进入新区域 ===== + if (toName != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] ★★★开始进入处理: " + + toName);*/ + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + plugin.getLogger().info( + "[防护-传送-进入] 取消旧延时, 旧区域=" + + oldP); + if (oldP != null) { + diffEffects(p, uid, oldP, toName); + } + } + + // 进入提示 + if (toArea != null && toArea.enterMsg != null + && !toArea.enterMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(toArea.enterMsg)); + } + + // 发放效果 + if (toArea != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] 调用applyRegionEffects");*/ + applyRegionEffects(p, uid, toName, toArea); +// 清除效果 + clearBadEffects(p, toArea); + + if (toArea.confiscateItems != null + && !toArea.confiscateItems.isEmpty() + && !isExemptFromConfiscation( + p, toName)) { + confiscateItems(p, toArea); + } + } + + // 强制模式 + if (toArea != null && toArea.enforceGameMode != null + && !toArea.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, toName)) { + GameMode target = GameMode.valueOf( + toArea.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + // 更新记录 + if (toName != null) { + playerCurrentArea.put(uid, toName); + } else { + playerCurrentArea.remove(uid); + } + } + + + public Map getPendingClearTasks() { + return pendingClearTask; + } + + public Map> + getPlayerMarkedEffects() { + return playerMarkedEffects; + } + + public void handleInside(Player p, AreaConfig ac) { + clearBadEffects(p, ac); + + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + // 全白或区白 → 跳过所有限制性规则(包括没收) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + handleConfiscate(p, ac); + } + + public void cleanupExpiredProtections() { + protectedEntities.entrySet().removeIf( + entry -> System.currentTimeMillis() > entry.getValue()); + } + + // 禁止袭击:只驱逐袭击怪物 + private void banRaidMobs(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(32, 32, 32)) { + String t = ent.getType().name(); + if (t.equals("PILLAGER") + || t.equals("VINDICATOR") + || t.equals("EVOKER") + || t.equals("RAVAGER") + || t.equals("VEX") + || t.equals("WITCH") + || t.equals("ILLUSIONER") + || t.equals("ZOMBIE_VILLAGER")) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(voidLoc.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + public Set getAreaNames() { + return areas.keySet(); + } + + public Collection getPendingTasks() { + return pendingClearTask.values(); + } + + + // 判断是否为远程攻击敌对生物 + private boolean isRangedHostile(String typeName) { + switch (typeName) { + // ===== 骷髅系 ===== + case "SKELETON": // 骷髅(弓箭) + case "STRAY": // 流浪者(迟缓之箭) + case "BOGGED": // 虫蚀骷髅(毒箭) + case "WITHER_SKELETON": // 凋灵骷髅(近战但算灾厄) + + // ===== 灾厄村民 ===== + case "PILLAGER": // 掠夺者(弩) + case "ILLUSIONER": // 幻术师(弓箭+失明) + case "EVOKER": // 幻魔者(唤魔) + case "VINDICATOR": // 卫道士(斧头近战) + case "VEX": // 恼鬼(剑近战,但会穿墙飞行) + + // ===== 法术/投射 ===== + case "BLAZE": // 烈焰人(火球) + case "WITCH": // 女巫(药水) + + // ===== 飞行/爆炸 ===== + case "GHAST": // 恶魂(火球) + case "PHANTOM": // 幻翼(俯冲) + case "BIG_GHAST": // 大恶魂(1.21新增) + + // ===== 水下 ===== + case "GUARDIAN": // 守卫者(激光) + case "ELDER_GUARDIAN": // 远古守卫者(激光) + + // ===== 远程特殊 ===== + case "SHULKER": // 潜影贝(潜影弹) + case "BREEZE": // 旋风人(风弹) + + // ===== 蜘蛛系(远程毒液攻击) ===== + case "SPIDER": // 蜘蛛(白天远程吐丝) + case "CAVE_SPIDER": // 洞穴蜘蛛(毒) + + // ===== 1.21新增 ===== + case "WIND_CHARGED": // 蓄风怪(风弹) + case "WEAVING": // 盘丝怪(吐丝) + case "OOZING": // 渗浆怪(史莱姆) + case "INFESTED": // 寄生怪(蠹虫) + + // ===== 盔甲/变种(部分版本) ===== + case "ARMORED_SKELETON": // 盔甲骷髅 + case "ARCHER_SKELETON": // 弓箭骷髅(部分版本) + case "SNOW_GOLEM": // 雪傀儡(不是敌对但远程) + + // ===== 亡灵骑乘 ===== + case "HUSK": // 尸壳(近战但属于灾厄系) + case "DROWNED": // 溺尸(三叉戟远程) + case "ZOMBIE_VILLAGER": // 僵尸村民 + case "ZOMBIE_PIGMAN": // 僵尸猪灵 + case "ZOMBIFIED_PIGLIN": // 僵尸猪灵(1.16+) + case "PIGLIN_BRUTE": // 猪灵蛮兵 + + // ===== 猪灵(弓箭) ===== + case "PIGLIN": // 猪灵(金弩远程) + + // ===== 末影系 ===== + case "ENDER_DRAGON": // 末影龙(远程吐息) + case "WARDEN": // 监守者(远程音波) + + // ===== 其他 ===== + case "CREPER": // 苦力怕(爆炸不算远程但危险) + case "CREEPER": // 苦力怕 + + return true; + default: + return false; + } + } + + + + private void applyEffects(Player p, AreaConfig ac) { + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + + PotionEffectType type = resolveEffectType(effName); + plugin.getLogger().info("[防护调试] 给予: " + + effName + " -> " + type); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + plugin.getLogger().info("[防护调试] 已给予: " + + effName + " 等级" + level + + " 持续" + duration + "秒"); + } + } + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockPlace + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止放置方块"); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockBreak + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏方块"); + } + } + + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + // 保护性规则:对所有人无条件生效 + if (ac.denyAllDamage) { e.setCancelled(true); return; } + if (ac.denyFallDamage + && e.getCause() == EntityDamageEvent.DamageCause.FALL) + e.setCancelled(true); + } + + + @EventHandler + public void onPVP(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyPVP + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + + @EventHandler + public void onFood(FoodLevelChangeEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyHunger) + e.setCancelled(true); + } + + + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyDrop + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止丢弃物品"); + } + } + + // 替换整个 onInteract 方法 + @EventHandler + public void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand == null) return; + + // ===== 选区工具 ===== + if (hand.getType() == Material.BREEZE_ROD) { + ItemMeta meta = hand.getItemMeta(); + if (meta != null && meta.getDisplayName() != null + && meta.getDisplayName().contains("区域选择")) { + Action action = e.getAction(); + boolean isLeft = (action == Action.LEFT_CLICK_AIR + || action == Action.LEFT_CLICK_BLOCK); + boolean isRight = (action == Action.RIGHT_CLICK_AIR + || action == Action.RIGHT_CLICK_BLOCK); + if (isLeft || isRight) { + e.setCancelled(true); + Block block = e.getClickedBlock(); + if (block == null) { + try { block = p.getTargetBlockExact(5); } + catch (Exception ignored) {} + } + if (block == null) return; + UUID uid = p.getUniqueId(); + if (isLeft) { + pos1.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置1: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } else { + pos2.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置2: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } + } + return; + } + } + + // ===== 放置实体类物品时不检查(展示框、矿车、船、地图等)===== + Material mat = hand.getType(); + if (mat == Material.ITEM_FRAME + || mat == Material.GLOW_ITEM_FRAME + || mat == Material.MINECART + || mat == Material.TNT_MINECART + || mat == Material.CHEST_MINECART + || mat == Material.HOPPER_MINECART + || mat == Material.OAK_BOAT + || mat == Material.SPRUCE_BOAT + || mat == Material.BIRCH_BOAT + || mat == Material.JUNGLE_BOAT + || mat == Material.ACACIA_BOAT + || mat == Material.DARK_OAK_BOAT + || mat == Material.MANGROVE_BOAT + || mat == Material.CHERRY_BOAT + || mat == Material.BAMBOO_RAFT + || mat == Material.MAP + || mat == Material.ARMOR_STAND) { + return; + } + + // ===== 区域规则检查 ===== + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + + // 末影珍珠禁止 + if (ac.denyEnderPearl + && hand.getType() == Material.ENDER_PEARL) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用末影珍珠"); + return; + } + + // 弓箭禁止 + if (ac.denyBow + && (hand.getType() == Material.BOW + || hand.getType() == Material.CROSSBOW)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用弓箭"); + return; + } + + // 白名单跳过 + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 禁止使用物品 + String typeId = hand.getType().name(); + if (ac.denyUseItems.contains(typeId)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用此物品"); + return; + } + + // 没收检查 + handleConfiscate(p, ac); + } + + + + @EventHandler + public void onExplosion(EntityExplodeEvent e) { + Iterator it = e.blockList().iterator(); + while (it.hasNext()) { + Block b = it.next(); + AreaConfig ac = getArea( + b.getWorld().getName(), + b.getX(), b.getY(), b.getZ()); + if (ac != null && ac.denyExplosion) + it.remove(); + } + } + public void loadWhitelists() { + globalPlayerWhitelist.clear(); + areaPlayerWhitelist.clear(); + + if (!whitelistDir.exists()) { + whitelistDir.mkdirs(); + return; + } + + try { + // 加载全局白名单 + if (globalWhitelistFile.exists()) { + List lines = + java.nio.file.Files.readAllLines( + globalWhitelistFile.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + globalPlayerWhitelist.add(name); + } + } + } + + // 加载各区域白名单 + File[] files = whitelistDir.listFiles( + (File d, String n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (files != null) { + for (File f : files) { + String areaName = f.getName() + .replace(".txt", ""); + Set set = + ConcurrentHashMap.newKeySet(); + List lines = + java.nio.file.Files.readAllLines( + f.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + set.add(name); + } + } + if (!set.isEmpty()) { + areaPlayerWhitelist.put( + areaName, set); + } + } + } + + plugin.getLogger().info( + "[防护] 白名单加载完成,全局: " + + globalPlayerWhitelist.size() + + ",区域数: " + + areaPlayerWhitelist.size()); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载白名单失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onPotionSplash(PotionSplashEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + AreaConfig ac = getArea( + shooter.getWorld().getName(), + shooter.getLocation().getBlockX(), + shooter.getLocation().getBlockY(), + shooter.getLocation().getBlockZ()); + if (ac != null && ac.denyPotion + && !isPlayerWhitelisted(shooter.getName(), ac)) { + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止使用药水"); + } + } + + /** + * 解析区域名:先精准匹配,再模糊匹配 + * @param input 用户输入的区域名 + * @return 匹配到的区域名,无匹配返回null + */ + public String resolveAreaName(String input) { + if (input == null || input.isEmpty()) return null; + if (input.equalsIgnoreCase("global")) return "global"; + + // 第一步:精准匹配 + for (String name : areas.keySet()) { + if (name.equalsIgnoreCase(input)) { + return name; + } + } + + // 第二步:包含匹配(用户只打了部分名字) + for (String name : areas.keySet()) { + if (name.contains(input) || input.contains(name)) { + return name; + } + } + + // 第三步:编辑距离模糊匹配(著称→主城) + String bestMatch = null; + int bestDistance = Integer.MAX_VALUE; + for (String name : areas.keySet()) { + int dist = levenshteinDistance( + input.toLowerCase(), name.toLowerCase()); + if (dist < bestDistance) { + bestDistance = dist; + bestMatch = name; + } + } + + // 容错阈值:编辑距离 <= 名字长度的一半 + if (bestMatch != null) { + int threshold = Math.max(1, bestMatch.length() / 2); + if (bestDistance <= threshold) { + return bestMatch; + } + } + + return null; + } + + /** + * 判断字符串是否是区域名(精准或模糊) + */ + private boolean isAreaName(String name) { + if (name.equalsIgnoreCase("global")) return false; + return resolveAreaName(name) != null; + } + + /** + * 获取模糊匹配的实际区域名(用于提示用户) + */ + private String getResolvedName(String input) { + return resolveAreaName(input); + } + + /** + * 编辑距离算法(莱文斯坦距离) + */ + private int levenshteinDistance(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + for (int i = 0; i <= s1.length(); i++) dp[i][0] = i; + for (int j = 0; j <= s2.length(); j++) dp[0][j] = j; + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], + Math.min(dp[i - 1][j], dp[i][j - 1])); + } + } + } + return dp[s1.length()][s2.length()]; + } + + /** + * 智能重排参数:找出子命令位置,重排为标准顺序 + * 标准顺序:/protect 子命令 参数1 参数2 ... + */ + private String[] smartReorderArgs(String[] original) { + if (original.length <= 1) return original; + + Set knownSubs = new HashSet<>(Arrays.asList( + "add", "remove", "additem", "removeitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite", + "list", "listitem", "创建", "删除", "重载", + "工具", "expand", "contraction", + "on", "off", "tempon", "modeexempt" + )); + + String first = original[0].toLowerCase(); + if (knownSubs.contains(first)) { + return original; + } + + int subIndex = -1; + for (int i = 0; i < original.length; i++) { + if (knownSubs.contains(original[i].toLowerCase())) { + subIndex = i; + break; + } + } + + if (subIndex < 0) { + return original; + } + + String[] reordered = new String[original.length]; + reordered[0] = original[subIndex]; + int idx = 1; + for (int i = 0; i < original.length; i++) { + if (i == subIndex) continue; + reordered[idx] = original[i]; + idx++; + } + + return reordered; + } + + // ==================== 管理命令 ==================== + public boolean handleCommand(CommandSender sender, + String[] args) { + args = smartReorderArgs(args); + if (args.length == 0) { + showHelp(sender); + return true; + } + String sub = args[0].toLowerCase(); +// ===== 诊断命令 ===== + if (sub.equals("sdf1debug")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + sender.sendMessage("§e===== 调试信息 ====="); + sender.sendMessage("§7当前区域: " + + playerCurrentArea.get(uid)); + sender.sendMessage("§7标记效果数: " + + (playerMarkedEffects.containsKey(uid) + ? playerMarkedEffects.get(uid).size() + : "无标记")); + sender.sendMessage("§7待清理任务: " + + pendingClearTask.containsKey(uid)); + sender.sendMessage("§7区域数: " + areas.size()); + + if (playerMarkedEffects.containsKey(uid)) { + List marked = + playerMarkedEffects.get(uid); + for (PotionEffectType type : marked) { + sender.sendMessage("§7 标记: " + + type.getName() + + " 身上有=" + + (p.getPotionEffect(type) != null)); + } + } + return true; + } + +// ===== 强制清理命令(测试用)===== + if (sub.equals("testclear")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + List marked = + playerMarkedEffects.remove(uid); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + sender.sendMessage("§a已强制清理 " + + marked.size() + " 个效果"); + } else { + sender.sendMessage("§c无标记效果可清理"); + } + + // 取消延时任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + sender.sendMessage("§a已取消延时任务"); + } + + removePlayerEffects(uid, + playerCurrentArea.getOrDefault(uid, "")); + sender.sendMessage("§aDB已清理"); + return true; + } + + // ===== 工具 ===== + if (sub.equals("工具") || sub.equals("wand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + ItemStack tool = new ItemStack(WAND); + ItemMeta meta = tool.getItemMeta(); + meta.setDisplayName("§a§l区域选择工具"); + meta.setLore(Arrays.asList( + "§7左键: 位置1", "§7右键: 位置2")); + tool.setItemMeta(meta); + p.getInventory().addItem(tool); + p.sendMessage("§a§l[防护] §f已获取选择工具"); + return true; + } + + // ===== 创建 ===== + if (sub.equals("创建") || sub.equals("create")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 创建 <名>"); + return true; + } + Player p = (Player) sender; + UUID u = p.getUniqueId(); + if (!pos1.containsKey(u) + || !pos2.containsKey(u)) { + p.sendMessage("§c请先用工具选点"); + return true; + } + String areaName = args[1]; + File f = new File(rootDir, areaName + ".txt"); + if (f.exists()) { + p.sendMessage("§c区域已存在"); + return true; + } + Location l1 = pos1.get(u); + Location l2 = pos2.get(u); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 区域: " + areaName); + pw.println("起点: " + l1.getWorld().getName() + + "," + l1.getBlockX() + + "," + l1.getBlockZ()); + pw.println("终点: " + l2.getWorld().getName() + + "," + l2.getBlockX() + + "," + l2.getBlockZ()); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("# 禁止放置方块"); + pw.println("# 禁止破坏方块"); + pw.println("# 禁止PVP"); + pw.println("# 效果: 夜视 1 999"); + pw.println("# 进入提示: 欢迎来到保护区"); + pw.println("# 离开提示: 已离开保护区"); + pw.close(); + p.sendMessage("§a§l[防护] §f区域 " + + areaName + " 已创建"); + loadAllAreas(); + } catch (IOException ex) { + p.sendMessage("§c创建失败"); + } + return true; + } + + // ===== 和平白名单 ===== + if (sub.equals("addname")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别: " + + args[1] + " 或 " + args[2]); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 和平白名单"); + return true; + } + sender.sendMessage("§c用法: /protect addname " + + "<区域> <名字>"); + return true; + } + if (sub.equals("removename")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + parsed[0]); + return true; + } + sender.sendMessage("§c用法: /protect removename " + + "<区域> <名字>"); + return true; + } + if (sub.equals("listname")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listname <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 和平白名单:"); + if (ac.peaceWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.peaceWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 模式排除 ===== + if (sub.equals("addwhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect addwhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("removewhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect removewhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("listwhite")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listwhite <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 模式排除名单:"); + if (ac.modeExempt.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.modeExempt) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 列表 ===== + if (sub.equals("列表") || sub.equals("list")) { + if (args.length == 2) { + if (args[1].equalsIgnoreCase("global")) { + sender.sendMessage("§a全局白名单:"); + if (globalPlayerWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n + : globalPlayerWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + String an = args[1]; + Set wl = + areaPlayerWhitelist.get(an); + sender.sendMessage("§a" + an + " 白名单:"); + if (wl == null || wl.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : wl) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + if (areas.isEmpty()) { + sender.sendMessage("§7暂无区域"); + } else { + sender.sendMessage("§e§l区域列表:"); + for (Map.Entry en + : areas.entrySet()) { + sender.sendMessage("§a " + en.getKey() + + " §7规则:" + + en.getValue().ruleCount() + + "条"); + } + } + return true; + } + + // ===== 重载 ===== + if (sub.equals("重载") || sub.equals("reload")) { + reload(); + sender.sendMessage("§a已重载 " + + areas.size() + " 个区域"); + return true; + } + + // ===== 物品黑名单 ===== + if (sub.equals("additem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + args[1]); + return true; + } + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + itemName); + return true; + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + } else { + areaItemBlacklist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()) + .add(itemName); + sender.sendMessage("§a已加黑: " + + itemName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect additem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("removeitem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (!globalItemBlacklist + .contains(itemName)) { + sender.sendMessage( + "§c全局黑名单中没有: " + + itemName); + return true; + } + globalItemBlacklist.remove(itemName); + sender.sendMessage("§a已移除: " + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.remove(itemName); + } else { + Set list = + areaItemBlacklist.get(areaName); + if (list != null) { + list.remove(itemName); + } + } + sender.sendMessage("§a已移除: " + + itemName); + return true; + } + sender.sendMessage("§c用法: /protect removeitem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("listitem")) { + if (args.length < 2) { + sender.sendMessage( + "§a全局物品黑名单:"); + if (globalItemBlacklist.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item + : globalItemBlacklist) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + Set list = + areaItemBlacklist.get(args[1]); + sender.sendMessage("§a" + args[1] + + " 物品黑名单:"); + if (list == null || list.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item : list) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + + // ===== expand 扩建 ===== + if (sub.equals("expand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ -= amount; + else if (dir.equals("南")) maxZ += amount; + else if (dir.equals("东")) maxX += amount; + else if (dir.equals("西")) minX -= amount; + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f扩建 §e" + + amount + "§f格"); + return true; + } + + // ===== contract 收缩 ===== + if (sub.equals("contraction") + || sub.equals("contract")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ += amount; + else if (dir.equals("南")) maxZ -= amount; + else if (dir.equals("东")) maxX -= amount; + else if (dir.equals("西")) minX += amount; + if (minX > maxX || minZ > maxZ) { + p.sendMessage("§c收缩过度"); + return true; + } + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f收缩 §e" + + amount + "§f格"); + return true; + } + + // ===== 删除 ===== + if (sub.equals("删除") || sub.equals("delete")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 删除 <名>"); + return true; + } + File f = new File(rootDir, args[1] + ".txt"); + if (f.exists()) { + f.delete(); + areas.remove(args[1]); + sender.sendMessage("§a已删除: " + args[1]); + } else { + sender.sendMessage("§c不存在: " + args[1]); + } + return true; + } + + // ===== add 加白名单 ===== + if (sub.equals("add")) { + if (args.length == 2) { + String name = args[1]; + if (globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 已在全局白名单中"); + return true; + } + globalPlayerWhitelist.add(name); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + + playerName); + } else { + Set wl = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()); + wl.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已加白: " + + playerName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect add " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== remove 移除白名单 ===== + if (sub.equals("remove")) { + if (args.length == 2) { + String name = args[1]; + if (!globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 不在全局白名单中"); + return true; + } + globalPlayerWhitelist.remove(name); + saveWhitelists(); + sender.sendMessage("§a已从全局白名单移除: " + + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.remove(playerName); + saveWhitelists(); + sender.sendMessage("§a已移除: " + + playerName); + } else { + Set wl = + areaPlayerWhitelist.get(areaName); + if (wl != null) { + wl.remove(playerName); + } + saveWhitelists(); + sender.sendMessage("§a已从 " + areaName + + " 移除: " + playerName); + } + return true; + } + sender.sendMessage("§c用法: /protect remove " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== on/off/tempon 边框 ===== + if (sub.equals("on")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, false); + p.sendMessage("§a§l[防护] §f已显示 " + + ac.name + " 边框"); + return true; + } + if (sub.equals("off")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + cancelBorder((Player) sender); + ((Player) sender).sendMessage( + "§a§l[防护] §f已关闭边框显示"); + return true; + } + if (sub.equals("tempon")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, true); + p.sendMessage("§a§l[防护] §f临时显示 " + + ac.name + " 边框(15秒)"); + return true; + } + + showHelp(sender); + return true; + } + + + private String[] parseAreaAndTarget(String arg1, String arg2) { + String r1 = resolveAreaName(arg1); + if (r1 != null) { + return new String[]{r1, arg2}; + } + String r2 = resolveAreaName(arg2); + if (r2 != null) { + return new String[]{r2, arg1}; + } + return null; + } + + private boolean playerExists(String playerName) { + // 在线玩家 + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.getName().equalsIgnoreCase(playerName)) return true; + } + // 离线玩家 + @SuppressWarnings("deprecation") + OfflinePlayer op = Bukkit.getOfflinePlayer(playerName); + return op.hasPlayedBefore(); + } + + + private boolean validateArea(CommandSender sender, String areaName) { + if (!areas.containsKey(areaName)) { + sender.sendMessage("§c区域不存在: " + areaName); + return false; + } + return true; + } + + + private String matname(Material mat) { + String name = mat.name(); + String[] parts = name.split("_"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (sb.length() > 0) sb.append(" "); + sb.append(part.substring(0, 1).toUpperCase()) + .append(part.substring(1).toLowerCase()); + } + return sb.toString(); + } + private String yawToDir(float yaw) { + // MC中: 0=南, 90=西, 180=北, -90/270=东 + yaw = yaw % 360; + if (yaw < 0) yaw += 360; + if (yaw >= 315 || yaw < 45) return "南"; + if (yaw >= 45 && yaw < 135) return "西"; + if (yaw >= 135 && yaw < 225) return "北"; + return "东"; + } + private void saveAreaConfig(AreaConfig ac) { + File f = new File(rootDir, ac.name + ".txt"); + if (!f.exists()) return; + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + if (line.trim().startsWith("起点:")) { + lines.add("起点: " + ac.world + + "," + ac.x1 + "," + ac.z1); + } else if (line.trim().startsWith("终点:")) { + lines.add("终点: " + ac.world + + "," + ac.x2 + "," + ac.z2); + } else { + lines.add(line); + } + } + br.close(); + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + for (String l : lines) pw.println(l); + pw.close(); + } catch (IOException ignored) {} + } + + private void showHelp(CommandSender s) { + s.sendMessage("§e§l==== 区域防护 ===="); + s.sendMessage("§a/protect 工具 §7选地工具"); + s.sendMessage("§a/protect 创建 <名> §7创建区域"); + s.sendMessage("§a/protect 列表 §7区域列表"); + s.sendMessage("§a/protect 重载 §7重载配置"); + s.sendMessage("§a/protect 删除 <名> §7删除区域"); + s.sendMessage("§a/protect add [区域] <玩家> §7玩家加白"); + s.sendMessage("§a/protect remove [区域] <玩家> §7玩家删白"); + s.sendMessage("§a/protect additem [区域] <物品> §7物品加黑"); + s.sendMessage("§a/protect removeitem [区域] <物品> §7物品删黑"); + s.sendMessage("§a/protect on §7显示边框"); + s.sendMessage("§a/protect off §7关闭边框"); + s.sendMessage("§a/protect tempon §7临时边框(15秒)"); + s.sendMessage("§a/protect list §7列出全局白名单"); + s.sendMessage("§a/protect list <区域> §7列出区域白名单"); + s.sendMessage("§a/protect listitem §7列出全局物品黑名单"); + s.sendMessage("§a/protect listitem <区域> §7列出区域物品黑名单"); + + } +// ==================== 白名单管理公共方法 ==================== + + // ==================== 白名单操作 ==================== + + public boolean isPlayerGlobalWhitelisted( + String playerName) { + return globalPlayerWhitelist.contains(playerName); + } + + public boolean addPlayerToGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean isPlayerAreaWhitelisted( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + return wl != null && wl.contains(playerName); + } + + public boolean addPlayerToAreaWhitelist( + String areaName, String playerName) { + Set set = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap.newKeySet()); + boolean ok = set.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromAreaWhitelist( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + boolean ok = wl != null && wl.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + + + public int handleConfiscate(Player p, AreaConfig ac) { + if (isPlayerWhitelisted(p.getName(), ac)) return 0; + Set allItems = new HashSet<>(ac.confiscateItems); + allItems.addAll(globalItemBlacklist); + Set areaList = areaItemBlacklist.get(ac.name); + if (areaList != null) allItems.addAll(areaList); + + if (allItems.isEmpty()) return 0; + + int totalRemoved = 0; + String confiscatedNames = ""; + + for (String itemId : allItems) { + // 过滤掉区域名和无效ID + Material mat = Material.matchMaterial(itemId); + if (mat == null) { + plugin.getLogger().warning( + "[防护] 跳过无效物品: " + itemId); + continue; + } + ItemStack[] all = p.getInventory().getContents(); + int count = 0; + for (int i = 0; i < all.length; i++) { + if (all[i] != null && all[i].getType() == mat) { + count += all[i].getAmount(); + all[i] = null; + } + } + p.getInventory().setContents(all); + + ItemStack off = p.getInventory().getItemInOffHand(); + if (off != null && off.getType() == mat) { + count += off.getAmount(); + p.getInventory().setItemInOffHand(null); + } + + if (count > 0) { + totalRemoved += count; + if (!confiscatedNames.isEmpty()) + confiscatedNames += "§7、§e"; + confiscatedNames += "§e" + matname(mat) + + " x" + count; + } + } + + if (totalRemoved > 0) { + String msg = (ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty()) + ? ac.confiscateMsg + : "你携带了违禁品,已被没收"; + p.sendMessage("§c§l[区域防护] §f" + msg); + p.sendMessage("§7没收物品: " + confiscatedNames); + + if (ac.enableAnnounce + && ac.announceTemplate != null + && !ac.announceTemplate.isEmpty()) { + String ann = ac.announceTemplate + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)) + .replace("{items}", confiscatedNames); + Bukkit.broadcastMessage("§c§l[区域防护] §f" + ann); + } + for (String cmd : ac.punishCommands) { + String finalCmd = cmd + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)); + Bukkit.getScheduler().runTask(plugin, + () -> Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), finalCmd)); + } + } + return totalRemoved; + } + public void clearBlacklists() { + globalItemBlacklist.clear(); + areaItemBlacklist.clear(); + plugin.getLogger().info("[防护] 已清空所有黑名单"); + } + + @EventHandler + public void onEntityDamage( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑 ===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + // 展示框被点击(旋转/放入/取出) + @EventHandler + public void onItemFrameInteract( + PlayerInteractAtEntityEvent e) { + Entity clicked = e.getRightClicked(); + String typeName = clicked.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + Player p = e.getPlayer(); + + // 全局白放行 + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(clicked); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + // 非白名单 → 拦截 + // 记录当前旋转角度 + final Rotation savedRotation; + final Entity frame = clicked; + if (clicked instanceof org.bukkit.entity.ItemFrame) { + savedRotation = + ((org.bukkit.entity.ItemFrame) clicked) + .getRotation(); + } else { + savedRotation = null; + } + + e.setCancelled(true); + + // 下一tick强制恢复旋转 + if (savedRotation != null) { + final Rotation restore = savedRotation; + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (frame.isValid() + && frame instanceof org.bukkit.entity.ItemFrame) { + ((org.bukkit.entity.ItemFrame) frame) + .setRotation(restore); + } + }, 1L); + } + + p.sendMessage("§c§l[区域防护] §f禁止交互展示框"); + + // 驱逐远程敌对生物 + banRangedHostiles(p, ac); + } + + + + // 展示框保护:检查展示框本身+周围方块所属区域 + + // ===== 展示框攻击拦截(左键/投射物破坏)===== + @EventHandler + public void onEntityDamageByEntity( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + // 展示框被攻击 + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑(原有的,不动)===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + private AreaConfig findFrameArea(Entity frame) { + Location loc = frame.getLocation(); + int bx = loc.getBlockX(); + int by = loc.getBlockY(); + int bz = loc.getBlockZ(); + String world = loc.getWorld().getName(); + + AreaConfig ac = getArea(world, bx, by, bz); + if (ac != null && ac.denyItemFrame) return ac; + + int[][] offsets = { + {0, 0, 1}, {0, 0, -1}, + {1, 0, 0}, {-1, 0, 0}, + {0, 1, 0}, {0, -1, 0} + }; + for (int[] off : offsets) { + ac = getArea(world, + bx + off[0], by + off[1], bz + off[2]); + if (ac != null && ac.denyItemFrame) return ac; + } + return null; + } + + // ===== 投射物击中展示框(弓箭/弩)===== + @EventHandler + public void onProjectileHit( + org.bukkit.event.entity.ProjectileHitEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + Entity hit = e.getHitEntity(); + if (hit == null) return; + String typeName = hit.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(shooter.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(hit); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(shooter.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + } + + + // ===== 区域外火焰不能蔓延进区域 ===== + @EventHandler + public void onBlockSpread(BlockSpreadEvent e) { + Block block = e.getBlock(); + AreaConfig tgtArea = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (tgtArea != null && tgtArea.denyFireSpread) { + e.setCancelled(true); + } + } + + // ===== 区域内禁止点燃方块 ===== + @EventHandler + public void onIgnite(BlockIgniteEvent e) { + Player p = e.getPlayer(); + if (p == null) return; // 非玩家点燃的(闪电等) + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f此区域禁止点燃物品"); + } + } + + // ===== 区域内方块着火(TNT、岩浆等)也阻止 ===== + @EventHandler + public void onBlockBurn(BlockBurnEvent e) { + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) + e.setCancelled(true); + } + + + private void savePeaceWhitelist(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("和平") + && trimmed.contains("白名单")) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.peaceWhitelist.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存和平白名单失败: " + e.getMessage()); + } + } + + + @EventHandler + public void onPaintingPlace( + org.bukkit.event.player.PlayerBucketEmptyEvent e) { + // 暂不处理 + } + + + // ==================== 数据类 ==================== + + public static class AreaConfig { + public String name = ""; + public String world = ""; + public int x1, z1, x2, z2; + public int yMin = 0, yMax = 255; + public List confiscateItems = new ArrayList<>(); + public List denyUseItems = new ArrayList<>(); + public List clearEffects = new ArrayList<>(); // 清除单个效果名 + public boolean clearAllBadEffects = false; // 清除所有负面+中性 + public List giveEffects = new ArrayList<>(); // [效果名, 等级, 秒数] + public List punishCommands = new ArrayList<>(); + public boolean denyBlockPlace = false; + public boolean denyBlockBreak = false; + public boolean denyPVP = false; + public boolean denyFallDamage = false; + public boolean denyHunger = false; + public boolean denyAllDamage = false; + public boolean denyMount = false; + public boolean denyDrop = false; + public boolean denyEnderPearl = false; + public boolean denyBow = false; + public boolean denyPotion = false; + public boolean denyExplosion = false; + public boolean enableAnnounce = false; + public String announceTemplate = ""; + public String confiscateMsg = ""; + public String enterMsg = ""; + public String leaveMsg = ""; + public boolean denyRaid = false; + public boolean denyFireSpread = false; + public boolean denyAllEffects = false;; + public boolean peaceMode = false; + public boolean denyItemFrame = false; + public Set peaceWhitelist = new HashSet<>(); + public String enforceGameMode = null; // 强制游戏模式 + public Set modeExempt = new HashSet<>(); // 模式排除名单 + + + + + private static final Map EFFECT_MAP + = new HashMap<>(); + static { + // 负面效果 + EFFECT_MAP.put("缓慢", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("凋零", PotionEffectType.WITHER); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("霉运", PotionEffectType.UNLUCK); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("蓄风", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生", PotionEffectType.INFESTED); + // 中性效果 + EFFECT_MAP.put("不祥之兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("不祥征兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("袭击征兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("试炼征兆", PotionEffectType.TRIAL_OMEN); + // 正面效果 + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); + EFFECT_MAP.put("瞬间治疗", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("跳跃提升", PotionEffectType.JUMP_BOOST); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性提升", PotionEffectType.RESISTANCE); + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("生命提升", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("村庄英雄", PotionEffectType.HERO_OF_THE_VILLAGE); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); + EFFECT_MAP.put("潮涌能量", PotionEffectType.CONDUIT_POWER); + EFFECT_MAP.put("海豚的恩惠", PotionEffectType.DOLPHINS_GRACE); + + + // 带效果二字 + // 补充带"效果"后缀的别名 + EFFECT_MAP.put("黑暗效果", PotionEffectType.DARKNESS); + EFFECT_MAP.put("不祥之兆效果", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆效果", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆效果", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("缓慢效果", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("中毒效果", PotionEffectType.POISON); + EFFECT_MAP.put("凋零效果", PotionEffectType.WITHER); + EFFECT_MAP.put("失明效果", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿效果", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱效果", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("反胃效果", PotionEffectType.NAUSEA); + EFFECT_MAP.put("飘浮效果", PotionEffectType.LEVITATION); + EFFECT_MAP.put("发光效果", PotionEffectType.GLOWING); + EFFECT_MAP.put("霉运效果", PotionEffectType.UNLUCK); + EFFECT_MAP.put("蓄风效果", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝效果", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆效果", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生效果", PotionEffectType.INFESTED); + EFFECT_MAP.put("挖掘疲劳效果", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害效果", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("村庄英雄效果", PotionEffectType.HERO_OF_THE_VILLAGE); + } + + + public int ruleCount() { + int c = 0; + if (!confiscateItems.isEmpty()) c++; + if (denyBlockPlace) c++; + if (denyBlockBreak) c++; + if (denyPVP) c++; + if (denyFallDamage) c++; + if (denyHunger) c++; + if (denyAllDamage) c++; + if (denyDrop) c++; + if (denyMount) c++; + if (denyEnderPearl) c++; + if (denyBow) c++; + if (denyRaid) c++; + if (denyPotion) c++; + if (denyExplosion) c++; + if (denyFireSpread) c++; + if (denyAllEffects) c++; + if (!clearEffects.isEmpty()) c++; + if (clearAllBadEffects) c++; + if (!giveEffects.isEmpty()) c++; + if (enableAnnounce) c++; + if (!punishCommands.isEmpty()) c++; + if (!denyUseItems.isEmpty()) c++; + if (peaceMode) c++; + if (denyItemFrame) c++; + if (!peaceWhitelist.isEmpty()) c++; + + return c; + } + } + } \ No newline at end of file diff --git a/backup/src/main/java/Sdf1_login/BondManager.java b/backup/src/main/java/Sdf1_login/BondManager.java new file mode 100644 index 0000000..ea78152 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/BondManager.java @@ -0,0 +1,697 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.io.File; +import java.sql.*; +import java.util.*; + +public class BondManager { + + private final Main plugin; + private Connection db; + + // ===== 交易类型常量 ===== + public static final String TX_ADMIN_GIVE = "admin_give"; + public static final String TX_ADMIN_DEDUCT = "admin_deduct"; + public static final String TX_TRANSFER_OUT = "transfer_out"; + public static final String TX_TRANSFER_IN = "transfer_in"; + public static final String TX_REDEEM = "redeem"; + public static final String TX_SHOP_BUY = "shop_buy"; + public static final String TX_DAILY_SIGN = "daily_sign"; + public static final String TX_FREEZE = "freeze"; + public static final String TX_UNFREEZE = "unfreeze"; + + + public static final int MAX_WATER_DAYS = 14; + public static final int ADMIN_MAX_WATER_DAYS = 30; + + public BondManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + // ======================== 数据库初始化 ======================== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 债券余额(含冻结状态) + st.execute("CREATE TABLE IF NOT EXISTS bonds (" + + "player_name TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "status TEXT DEFAULT 'normal')"); + + // 兼容旧表 + try { + st.execute("ALTER TABLE bonds " + + "ADD COLUMN status " + + "TEXT DEFAULT 'normal'"); + } catch (SQLException ignored) {} + + // 旧流水(保留兼容) + st.execute("CREATE TABLE IF NOT EXISTS bond_log (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "reason TEXT DEFAULT ''," + + "time INTEGER DEFAULT 0)"); + + // 新交易流水表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "bond_transaction (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "type TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "target_player TEXT DEFAULT ''," + + "operator TEXT DEFAULT ''," + + "reason TEXT DEFAULT ''," + + "balance_after INTEGER DEFAULT 0," + + "time INTEGER DEFAULT 0)"); + + try { + st.execute("CREATE INDEX " + + "IF NOT EXISTS idx_tx_pt " + + "ON bond_transaction(" + + "player_name, time)"); + } catch (SQLException ignored) {} + try { + st.execute("ALTER TABLE bond_transaction " + + "ADD COLUMN balance_before " + + "INTEGER DEFAULT 0"); + } catch (SQLException ignored) {} + + + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[Bond] DB init failed: " + + e.getMessage(), e); + } + } + + // ======================== 账户状态 ======================== + + public boolean isFrozen(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return "frozen".equals(s); + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return false; + } + + public String getAccountStatus(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return s != null ? s : "normal"; + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return "normal"; + } + + public void freezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='frozen' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + public void unfreezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='normal' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + private void ensureAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR IGNORE INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,0,'normal')"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + // ======================== 余额查询 ======================== + + public int getBonds(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + int v = 0; + if (rs.next()) v = rs.getInt("amount"); + rs.close(); ps.close(); + return v; + } catch (SQLException e) { return 0; } + } + + /** 该玩家是否在 bond 表中有真实记录 */ + public boolean hasBondAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM bonds " + + "WHERE player_name=? " + + "AND amount > 0"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + boolean exists = rs.next(); + rs.close(); ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ======================== 增加债券 ======================== + + public int addBonds(String player, int amount) { + return addBondsInternal(player, amount, + TX_ADMIN_GIVE, "", "", + "管理员发放"); + } + + public int addBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return -1; + return addBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + private int addBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + int after = before + amount; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status)" + + " VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, amount); + return after; + } + + // ======================== 扣除债券 ======================== + + + + private boolean deductBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + if (before < amount) return false; + int after = before - amount; + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET amount=? " + + "WHERE player_name=?"); + ps.setInt(1, after); + ps.setString(2, player); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, -amount); + return true; + } + + // ======================== 流水写入 ======================== + + private void logTransaction(String player, + String type, + int amount, + String targetPlayer, + String operator, + String reason, + int balanceBefore, + int balanceAfter) { + try { + // 合并模式:shop_buy 30秒内同商品合并 + if ("shop_buy".equals(type) + && reason != null + && !reason.isEmpty()) { + long mergeWindow = + System.currentTimeMillis() - 30000L; + PreparedStatement chk = + db.prepareStatement( + "SELECT id, amount " + + "FROM bond_transaction " + + "WHERE player_name=? " + + "AND type=? " + + "AND reason=? " + + "AND time > ? " + + "ORDER BY time DESC " + + "LIMIT 1"); + chk.setString(1, player); + chk.setString(2, type); + chk.setString(3, reason); + chk.setLong(4, mergeWindow); + ResultSet rs = chk.executeQuery(); + if (rs.next()) { + int existId = rs.getInt("id"); + int existAmt = rs.getInt("amount"); + rs.close(); + chk.close(); + PreparedStatement upd = + db.prepareStatement( + "UPDATE bond_transaction " + + "SET amount=?, " + + "balance_after=?, " + + "time=? " + + "WHERE id=?"); + upd.setInt(1, existAmt + amount); + upd.setInt(2, balanceAfter); + upd.setLong(3, + System.currentTimeMillis()); + upd.setInt(4, existId); + upd.executeUpdate(); + upd.close(); + return; + } + rs.close(); + chk.close(); + } + + // 原始插入 + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_transaction " + + "(player_name, type, amount," + + " target_player, operator," + + " reason, balance_before," + + " balance_after, time)" + + " VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, type); + ps.setInt(3, amount); + ps.setString(4, + targetPlayer != null ? targetPlayer : ""); + ps.setString(5, + operator != null ? operator : ""); + ps.setString(6, + reason != null ? reason : ""); + ps.setInt(7, balanceBefore); + ps.setInt(8, balanceAfter); + ps.setLong(9, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + // ======================== 扣除债券 ======================== + + /** 完整版:含冻结检查 + 流水记录 */ + public boolean deductBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return false; + return deductBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + public boolean checkoutCart(String player, + String[] itemNames, + int[] subtotals) { + int total = 0; + for (int s : subtotals) total += s; + if (isFrozen(player)) return false; + + int before = getBonds(player); + if (before < total) return false; + int after = before - total; + + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?,'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + + int running = before; + for (int i = 0; i < itemNames.length; i++) { + int next = running - subtotals[i]; + logTransaction(player, "shop_buy", subtotals[i], + "", "商店系统", + "购物车购买-" + itemNames[i], + running, next); + running = next; + } + + logLegacy(player, -total); + return true; + } + + public boolean setBonds(String player, int amount) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setInt(3, amount); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return true; + } + + // ======================== 转账 ======================== + + public TransferResult transfer(String from, + String to, + int amount) { + if (from.equalsIgnoreCase(to)) + return new TransferResult(false, + "不能给自己转账"); + if (isFrozen(from)) + return new TransferResult(false, + "你的账户已被冻结"); + if (isFrozen(to)) + return new TransferResult(false, + to + " 的账户已被冻结"); + if (isSameIP(from, to)) + return new TransferResult(false, + "不能与同IP玩家转账"); + + if (!deductBondsInternal(from, amount, + TX_TRANSFER_OUT, to, from, + "转账给" + to)) + return new TransferResult(false, "债券不足"); + + addBondsInternal(to, amount, + TX_TRANSFER_IN, from, from, + "收到来自" + from + "的转账"); + + return new TransferResult(true, "转账成功"); + } + + // ======================== 同IP检测 ======================== + + public boolean isSameIP(String p1, String p2) { + String ip1 = getPlayerIPAny(p1); + String ip2 = getPlayerIPAny(p2); + if (ip1 == null || ip2 == null) return false; + return ip1.equals(ip2); + } + + /** 获取同IP下的所有其他在线玩家 */ + public List getSameIPPlayers(String player) { + String ip = getPlayerIPAny(player); + if (ip == null) return Collections.emptyList(); + List list = new ArrayList<>(); + for (Player p : + plugin.getServer().getOnlinePlayers()) { + String pIp = plugin.getPlayerIP(p); + if (pIp != null && pIp.equals(ip) + && !p.getName() + .equalsIgnoreCase(player)) { + list.add(p.getName()); + } + } + return list; + } + + private String getPlayerIPAny(String player) { + // 在线:直接取 + Player p = plugin.getServer() + .getPlayerExact(player); + if (p != null) { + String ip = plugin.getPlayerIP(p); + if (ip != null) return ip; + } + // 离线:查主库 + try { + Object v = plugin.getDb() + .getField(player, "last_login_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + try { + Object v = plugin.getDb() + .getField(player, "register_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + return null; + } + + // ======================== 流水查询 ======================== + + public List> getTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } +// 放在 getTransactions 方法后面: + + /** + * 获取该玩家全部流水(不限时间,供打印使用) + */ + public List> + getAllTransactions(String player) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "ORDER BY time ASC"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("player_name", + rs.getString("player_name")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } +// 放在 getAllTransactions 方法后面: + + /** + * 获取所有有流水记录的玩家名(去重) + */ + public List getAllPlayerNames() { + List list = new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT DISTINCT player_name " + + "FROM bond_transaction " + + "ORDER BY player_name ASC"); + while (rs.next()) { + list.add(rs.getString("player_name")); + } + rs.close(); st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTransferTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "AND (type=? OR type=?) " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ps.setString(3, TX_TRANSFER_OUT); + ps.setString(4, TX_TRANSFER_IN); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } + + + private void logLegacy(String player, int amount) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_log " + + "(player_name,amount,time) " + + "VALUES (?,?,?)"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + public Map getLog( + String player, int limit) { + Map info = new LinkedHashMap<>(); + info.put("balance", getBonds(player)); + return info; + } + + // ======================== 转账结果 ======================== + + public static class TransferResult { + public final boolean success; + public final String message; + public TransferResult(boolean success, + String message) { + this.success = success; + this.message = message; + } + } +} diff --git a/backup/src/main/java/Sdf1_login/BondPrinter.java b/backup/src/main/java/Sdf1_login/BondPrinter.java new file mode 100644 index 0000000..674ec3c --- /dev/null +++ b/backup/src/main/java/Sdf1_login/BondPrinter.java @@ -0,0 +1,378 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; + +public class BondPrinter { + + private final Main plugin; + private final File outputDir; + + private static final SimpleDateFormat FILE_SDF = + new SimpleDateFormat( + "yyyy-MM-dd_HH-mm-ss"); + private static final SimpleDateFormat CELL_SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + private static final LinkedHashMap + TYPE_NAMES = new LinkedHashMap<>(); + + static { + TYPE_NAMES.put("admin_give", "管理员发放"); + TYPE_NAMES.put("admin_deduct", "管理员扣除"); + TYPE_NAMES.put("transfer_out", "转出"); + TYPE_NAMES.put("transfer_in", "转入"); + TYPE_NAMES.put("redeem", "CDK兑换"); + TYPE_NAMES.put("shop_buy", "商城购买"); + TYPE_NAMES.put("shop_sell", "商城出售"); // ← 补上 + TYPE_NAMES.put("daily_sign", "签到奖励"); + TYPE_NAMES.put("freeze", "冻结"); + TYPE_NAMES.put("unfreeze", "解冻"); + + } + + public BondPrinter(Main plugin) { + this.plugin = plugin; + this.outputDir = new File( + plugin.getDataFolder(), "printer"); + if (!this.outputDir.exists()) { + this.outputDir.mkdirs(); + } + } + + // ============ 打印单个玩家 ============ + + public void printPlayer(String player, + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintPlayer( + player, sender)); + } + + private void doPrintPlayer(String player, + CommandSender sender) { + try { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + + String fileName = player + "_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + deleteOld(player); + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, + escXml(player)); + writeXmlRow(pw, + new String[]{ + "时间", "类型", + "金额", "对象", + "操作者", "理由", + "操作前余额", + "操作后余额"}); + + for (Map tx : txs) { + writeXmlRow(pw, new String[]{ + txTime(tx), + txTypeName(tx), + String.valueOf( + txInt(tx, + "amount")), + txStr(tx, + "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")) + }); + } + + writeXmlFooter(pw); + pw.flush(); + } finally { + pw.close(); + } + + if (sender != null) { + sender.sendMessage("§a[打印] §f" + + player + " §a流水已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + txs.size() + + " §7条,§724小时后删除"); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 打印全服 ============ + + public void printAll(CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintAll(sender)); + } + + private void doPrintAll(CommandSender sender) { + try { + List players = + plugin.getBonds() + .getAllPlayerNames(); + if (players.isEmpty()) { + if (sender != null) { + sender.sendMessage("§c没有流水数据"); + } + return; + } + + String fileName = "全服流水_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + // ===== 收集所有流水 ===== + // key = 时间戳+玩家+金额,用于去重 + // value = 一行数据 + List allRows = new ArrayList<>(); + Set seenKeys = new HashSet<>(); + + for (String player : players) { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + for (Map tx : txs) { + int txId = ((Number) tx.get("id")) + .intValue(); + String dedupKey = + String.valueOf(txId); + if (!seenKeys.add(dedupKey)) { + continue; + } + + String timeStr = txTime(tx); + long timeMs = 0; + Object t = tx.get("time"); + if (t != null) { + timeMs = + ((Number) t).longValue(); + } + + allRows.add(new String[]{ + player, + timeStr, + txTypeName(tx), + String.valueOf( + txInt(tx, "amount")), + txStr(tx, "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")), + // 额外存时间戳用于排序 + String.valueOf(timeMs) + }); + } + } + + // ===== 按时间戳排序 ===== + allRows.sort((a, b) -> { + long ta = Long.parseLong( + a[a.length - 1]); + long tb = Long.parseLong( + b[b.length - 1]); + return Long.compare(ta, tb); + }); + + // ===== 写入文件 ===== + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, "全服流水"); + writeXmlRow(pw, new String[]{ + "玩家", "时间", "类型", + "金额", "对象", "操作者", + "理由", "操作前余额", + "操作后余额" + }); + + for (String[] row : allRows) { + // 去掉最后的时间戳列 + String[] cells = + new String[row.length - 1]; + System.arraycopy(row, 0, + cells, 0, cells.length); + writeXmlRow(pw, cells); + } + + writeXmlFooter(pw); + pw.flush(); + + if (sender != null) { + sender.sendMessage( + "§a[打印] §f全服流水 §a已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + players.size() + + " 名玩家,§f" + + allRows.size() + " §7条"); + sender.sendMessage( + "§7按时间从早到晚排序"); + sender.sendMessage( + "§724小时后自动删除"); + } + } finally { + pw.close(); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 清理 ============ + + public void cleanupOldFiles() { + long now = System.currentTimeMillis(); + long maxAge = 24L * 60 * 60 * 1000; + File[] files = outputDir.listFiles( + (d, n) -> n.endsWith(".xls")); + if (files == null) { + return; + } + int count = 0; + for (File f : files) { + if (now - f.lastModified() > maxAge) { + if (f.delete()) { + count++; + } + } + } + if (count > 0) { + plugin.getLogger().info( + "[Printer] 清理 " + + count + " 个过期文件"); + } + } + + private void deleteOld(String player) { + File[] old = outputDir.listFiles( + (d, n) -> n.startsWith(player + "_") + && n.endsWith(".xls")); + if (old != null) { + for (File f : old) { + f.delete(); + } + } + } + + // ============ XML工具 ============ + + private void writeXmlHeader(PrintWriter pw, + String sheetName) { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(" "); + pw.println(" "); + } + + private void writeXmlRow(PrintWriter pw, + String[] cells) { + pw.println(" "); + for (String cell : cells) { + pw.println(" " + + "" + + escXml(cell != null ? cell : "") + + ""); + } + pw.println(" "); + } + + private void writeXmlFooter(PrintWriter pw) { + pw.println("
    "); + pw.println("
    "); + pw.println("
    "); + } + + private String escXml(String val) { + if (val == null) { + return ""; + } + return val.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + + private String txTime(Map tx) { + Object t = tx.get("time"); + if (t == null) return ""; + long ms = ((Number) t).longValue(); + return CELL_SDF.format(new Date(ms)); + } + + private String txTypeName( + Map tx) { + String type = (String) tx.get("type"); + return TYPE_NAMES.getOrDefault(type, type); + } + + private int txInt(Map tx, + String key) { + Object v = tx.get(key); + if (v == null) return 0; + return ((Number) v).intValue(); + } + + private String txStr(Map tx, + String key) { + Object v = tx.get(key); + return v != null ? v.toString() : ""; + } +} diff --git a/backup/src/main/java/Sdf1_login/CDKManager.java b/backup/src/main/java/Sdf1_login/CDKManager.java new file mode 100644 index 0000000..e07569f --- /dev/null +++ b/backup/src/main/java/Sdf1_login/CDKManager.java @@ -0,0 +1,293 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.nio.charset.StandardCharsets; + +public class CDKManager { + + private final Main plugin; + private Connection db; + private final Map pendingInputs + = new ConcurrentHashMap<>(); + + public CDKManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS cdk (" + + "code TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "type TEXT DEFAULT 'bond'," + + "used INTEGER DEFAULT 0," + + "used_by TEXT DEFAULT ''," + + "created_time INTEGER DEFAULT 0," + + "used_time INTEGER DEFAULT 0)"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[CDK] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== CDK兑换(供SDF1调用) ===== + + /** + * 兑换CDK,返回结果字符串 + * @return "success:金额:余额前:余额后" + * 或 "fail:原因" + */ + public String redeem(String code, String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount,type,used FROM cdk " + + "WHERE code=?"); + ps.setString(1, code); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + rs.close(); ps.close(); + return "fail:not_found"; + } + int amount = rs.getInt("amount"); + String type = rs.getString("type"); + int used = rs.getInt("used"); + rs.close(); ps.close(); + + if (used == 1) + return "fail:already_used"; + + if (plugin.getBonds() + .isFrozen(player)) + return "fail:frozen"; + + PreparedStatement up = db.prepareStatement( + "UPDATE cdk SET used=1," + + "used_by=?,used_time=? " + + "WHERE code=?"); + up.setString(1, player); + up.setLong(2, + System.currentTimeMillis()); + up.setString(3, code); + up.executeUpdate(); up.close(); + + if ("bond".equals(type)) { + int bef = plugin.getBonds() + .getBonds(player); + plugin.getBonds().addBonds(player, + amount, + BondManager.TX_REDEEM, + "", "CDK系统", + "CDK兑换: " + code); + int aft = plugin.getBonds() + .getBonds(player); + return "success:" + amount + + ":" + bef + ":" + aft; + } + return "fail:unknown_type"; + } catch (SQLException e) { + return "fail:" + e.getMessage(); + } + } + + // ===== 聊天输入系统 ===== + + public void requestInput(Player p, + String type, + String context) { + CDKInput input = new CDKInput(); + input.type = type; + input.context = context; + input.time = System.currentTimeMillis(); + pendingInputs.put(p.getName(), input); + } + + public boolean isListening(String playerName) { + return pendingInputs + .containsKey(playerName); + } + + public void onChat(Player p, String msg) { + CDKInput input = pendingInputs + .remove(p.getName()); + if (input == null) return; + + if (System.currentTimeMillis() + - input.time > 60000) { + p.sendMessage("§c输入超时"); + return; + } + + if ("cdk".equals(input.type)) { + String result = redeem( + msg.trim(), p.getName()); + if (result != null + && result.startsWith("success:")) { + String[] parts = result.split(":"); + int amt = Integer.parseInt(parts[1]); + int bef = Integer.parseInt(parts[2]); + int aft = Integer.parseInt(parts[3]); + p.sendMessage("§e§l[CDK] §f兑换成功!" + + "获得 §c" + amt + "§f债券"); + p.sendMessage("§7余额: §e" + bef + + " §7-> §a" + aft); + } else { + p.sendMessage("§c§l[CDK] §f" + + (result != null + ? result.replace("fail:", "") + : "无效的兑换码")); + } + return; + } + + if ("econ".equals(input.type)) { + handleEconInput(p, msg.trim(), + input.context); + return; + } + + if ("bond".equals(input.type)) { + handleBondInput(p, msg.trim(), + input.context); + } + } + + private void handleEconInput(Player admin, + String msg, + String target) { + net.milkbowl.vault.economy.Economy econ + = null; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy + .Economy.class); + if (reg != null) + econ = reg.getProvider(); + } catch (Exception ignored) {} + if (econ == null) { + admin.sendMessage("§c经济系统不可用"); + return; + } + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String numStr = + msg.substring(1).trim(); + double amount = + Double.parseDouble(numStr); + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + if (msg.startsWith("-")) { + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp == null) { + admin.sendMessage("§c" + target + + " 不在线"); + return; + } + if (!econ.has(tp, amount)) { + admin.sendMessage("§c余额不足"); + return; + } + econ.withdrawPlayer(tp, amount); + admin.sendMessage("§a扣款成功"); + } else { + econ.depositPlayer(target, amount); + admin.sendMessage("§a到账成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + private void handleBondInput(Player admin, + String msg, + String target) { + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String sign = msg.substring(0, 1); + String rest = + msg.substring(1).trim(); + String[] parts = + rest.split("\\s+", 2); + int amount = Integer.parseInt(parts[0]); + String reason = parts.length >= 2 + ? parts[1].trim() + : "管理员手动调整"; + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + String opName = admin.getName(); + String txType = "-".equals(sign) + ? BondManager.TX_ADMIN_DEDUCT + : BondManager.TX_ADMIN_GIVE; + + if ("-".equals(sign)) { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + if (plugin.getBonds().deductBonds( + target, amount, txType, + "", opName, reason)) { + admin.sendMessage("§a扣除成功"); + } else { + admin.sendMessage("§c债券不足"); + } + } else { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + plugin.getBonds().addBonds(target, + amount, txType, + "", opName, reason); + admin.sendMessage("§a发放成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + public void cancelInput(String player) { + pendingInputs.remove(player); + } + + static class CDKInput { + String type; + String context; + long time; + } +} diff --git a/backup/src/main/java/Sdf1_login/ChatFilterManager.java b/backup/src/main/java/Sdf1_login/ChatFilterManager.java new file mode 100644 index 0000000..59cb5de --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ChatFilterManager.java @@ -0,0 +1,612 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatFilterManager { + + private final JavaPlugin plugin; + private final List whitelistUrls = + new ArrayList<>(); + private final Set whitelistPlayers = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Map + punishmentRules = + new LinkedHashMap<>(); + private final Map + punishmentDurations = + new LinkedHashMap<>(); + private final Map + violationCount = + new ConcurrentHashMap<>(); + private final Map + mutedPlayers = + new ConcurrentHashMap<>(); + private final Map messages = + new LinkedHashMap<>(); + private boolean notifyAdmin = true; + private boolean notifyAll = false; + private int muteDuration = 300; + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean v) { + enabled = v; + } + + + private static final Pattern URL_PATTERN = + Pattern.compile( + "(?i)(?:https?://\\S+)" + + "|(?:(?:[a-zA-Z0-9]" + + "(?:[a-zA-Z0-9\\-]*" + + "[a-zA-Z0-9])?\\.)+" + + "[a-zA-Z]{2,})" + + "(?:/\\S*)?"); + private static final Pattern DOT_VARIANTS = + Pattern.compile( + "[\uff0e\u3002\u2025\u2026\u00b7]"); + private static final Pattern COLOR_CODES = + Pattern.compile( + "\u00a7[0-9a-fk-orA-FK-OR]"); + + private static final Set FILE_EXT; + static { + Set s = new HashSet<>( + Arrays.asList( + "txt", "yml", "yaml", + "json", "xml", "java", + "class", "jar", "png", + "jpg", "jpeg", "gif", + "bmp", "svg", "ico", + "webp", "mp3", "mp4", + "avi", "mkv", "wav", + "flac", "ogg", "zip", + "rar", "7z", "tar", + "gz", "cfg", "conf", + "ini", "log", "db", + "sql", "sh", "bat", + "html", "htm", "css", + "js", "md", "pdf", + "dll", "so", "exe", + "bin", "c", "cpp", + "py", "rb", "go", + "rs", "kt", "lua", + "php", "asp", "jsp")); + FILE_EXT = Collections.unmodifiableSet(s); + } + + public ChatFilterManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + // ========== 配置加载 ========== + + public void loadConfig() { + whitelistUrls.clear(); + whitelistPlayers.clear(); + punishmentRules.clear(); + punishmentDurations.clear(); + messages.clear(); + + File f = new File( + plugin.getDataFolder(), "chat.txt"); + if (!f.exists()) writeDefaultFile(f); + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("白名单") + || t.equals("白名单:")) { + section = "whitelist"; + continue; + } + if (t.equals("处罚规则") + || t.equals("处罚规则:")) { + section = "punishment"; + continue; + } + if (t.equals("白名单玩家") + || t.equals("白名单玩家:")) { + section = "players"; + continue; + } + if (t.contains(":")) { + String[] kv = t.split(":", 2); + String k = kv[0].trim(); + String v = kv[1].trim(); + if (equals("启用过滤") + || equals("enabled")) { + enabled = parseBool(v); + continue; + } + + if (k.equals("通知管理员")) { + notifyAdmin = parseBool(v); + continue; + } + if (k.equals("全服通报")) { + notifyAll = parseBool(v); + continue; + } + if (k.equals("禁言时长")) { + try { + muteDuration = + Integer.parseInt(v); + } catch (Exception ignored) { + } + continue; + } + } + switch (section) { + case "whitelist": + whitelistUrls.add( + t.toLowerCase()); + break; + case "punishment": + String[] parts = t.split(":"); + if (parts.length >= 2) { + try { + int cnt = + Integer.parseInt( + parts[0] + .trim()); + String type = + parts[1].trim() + .toLowerCase(); + int dur = + parts.length >= 3 + ? Integer.parseInt( + parts[2] + .trim()) + : muteDuration; + punishmentRules.put( + cnt, type); + punishmentDurations.put( + cnt, dur); + } catch (Exception ignored) { + } + } + break; + case "players": + whitelistPlayers.add(t); + break; + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[Sdf1_chat] 配置加载失败: " + + e.getMessage()); + } + + // 从消息.txt读取chat_开头的消息 + loadMessages(); + } + + private void loadMessages() { + File msgFile = new File( + plugin.getDataFolder(), + "消息.txt"); + if (!msgFile.exists()) return; + try (BufferedReader mr = + new BufferedReader( + new InputStreamReader( + new FileInputStream( + msgFile), + StandardCharsets.UTF_8))) { + String mline; + while ((mline = mr.readLine()) != null) { + mline = mline.trim(); + if (mline.isEmpty() + || mline.startsWith("#")) + continue; + // 支持 = 和 : 两种分隔符 + int eqIdx = mline.indexOf('='); + int coIdx = mline.indexOf(':'); + int splitIdx = -1; + if (eqIdx >= 0 && coIdx >= 0) { + splitIdx = Math.min(eqIdx, coIdx); + } else if (eqIdx >= 0) { + splitIdx = eqIdx; + } else if (coIdx >= 0) { + splitIdx = coIdx; + } + if (splitIdx < 0) continue; + String k = mline.substring(0, + splitIdx).trim(); + String v = mline.substring( + splitIdx + 1).trim(); + if (k.startsWith("chat_")) { + messages.put(k, v); + } + } + } catch (Exception ignored) { + } + } + + + private void writeDefaultFile(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("启用过滤: " + enabled); + pw.println("通知管理员: true"); + pw.println("全服通报: false"); + pw.println("禁言时长: 300"); + pw.println(); + pw.println("白名单:"); + pw.println("*.minecraft.net"); + pw.println("baidu.com"); + pw.println("github.com"); + pw.println(); + pw.println("处罚规则:"); + pw.println("1:warn:0"); + pw.println("2:warn:0"); + pw.println("3:mute:300"); + pw.println("5:mute:600"); + pw.println("8:kick:0"); + pw.println("10:ban:0"); + pw.println(); + pw.println("白名单玩家:"); + } catch (IOException ignored) { + } + } + + public void saveConfig() { + File f = new File( + plugin.getDataFolder(), "chat.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("通知管理员: " + notifyAdmin); + pw.println("全服通报: " + notifyAll); + pw.println("禁言时长: " + muteDuration); + pw.println(); + pw.println("白名单:"); + for (String u : whitelistUrls) + pw.println(u); + pw.println(); + pw.println("处罚规则:"); + for (Map.Entry entry + : punishmentRules.entrySet()) { + int dur = punishmentDurations + .getOrDefault(entry.getKey(), + muteDuration); + pw.println(entry.getKey() + ":" + + entry.getValue() + + ":" + dur); + } + pw.println(); + pw.println("白名单玩家:"); + for (String p : whitelistPlayers) + pw.println(p); + } catch (IOException e) { + plugin.getLogger().severe( + "[Sdf1_chat] 保存失败: " + + e.getMessage()); + } + } + + // ========== 消息 ========== + + public String msg(String key, + String... args) { + String template = messages.getOrDefault( + key, key); + for (int i = 0; i < args.length - 1; + i += 2) { + template = template.replace( + "{" + args[i] + "}", + args[i + 1]); + } + return template; + } + + // ========== URL检测 ========== + + private String normalizeMessage(String msg) { + msg = COLOR_CODES.matcher(msg) + .replaceAll(""); + msg = DOT_VARIANTS.matcher(msg) + .replaceAll("."); + return msg; + } + + public List extractUrls( + String message) { + String normalized = + normalizeMessage(message); + List urls = new ArrayList<>(); + Matcher m = URL_PATTERN + .matcher(normalized); + while (m.find()) { + String url = m.group().toLowerCase(); + url = url.replaceAll( + "[\\s.,;!?,。;!?、)》」』】" + + "\\]\\[\\((]+$", + ""); + if (!url.isEmpty()) urls.add(url); + } + return urls; + } + + public boolean isWhitelisted(String url) { + String clean = url + .replaceAll("^https?://", "") + .toLowerCase(); + int colon = clean.indexOf(':'); + int slash = clean.indexOf('/'); + if (colon > 0 && (slash < 0 + || colon < slash)) { + clean = clean.substring(0, colon); + } + slash = clean.indexOf('/'); + if (slash > 0) { + clean = clean.substring(0, slash); + } + for (String entry : whitelistUrls) { + String e = entry.toLowerCase().trim(); + if (e.startsWith("*.")) { + String suffix = e.substring(1); + if (clean.endsWith(suffix) + || clean.equals( + suffix.substring(1))) + return true; + } else { + if (clean.equals(e) + || clean.endsWith( + "." + e)) + return true; + } + } + return false; + } + + public boolean isLikelyDomain(String url) { + int lastDot = url.lastIndexOf('.'); + if (lastDot < 0 + || lastDot >= url.length() - 1) + return false; + String ext = url.substring(lastDot + 1) + .toLowerCase(); + return !FILE_EXT.contains(ext); + } + + // ========== 玩家操作 ========== + + public boolean isMuted(String name) { + Long expiry = mutedPlayers.get(name); + if (expiry == null) return false; + if (System.currentTimeMillis() + >= expiry) { + mutedPlayers.remove(name); + return false; + } + return true; + } + + public String fmtDuration(int seconds) { + if (seconds <= 0) return "0秒"; + int day = seconds / 86400; + int hr = (seconds % 86400) / 3600; + int min = (seconds % 3600) / 60; + int sec = seconds % 60; + StringBuilder sb = new StringBuilder(); + if (day > 0) sb.append(day).append("天"); + if (hr > 0) + sb.append(hr).append("小时"); + if (min > 0) + sb.append(min).append("分"); + if (sec > 0 || sb.length() == 0) + sb.append(sec).append("秒"); + return sb.toString(); + } + + public void applyPunishment(Player player, + int violation) { + String type = "warn"; + int duration = muteDuration; + int maxRule = 0; + for (Map.Entry entry + : punishmentRules.entrySet()) { + if (violation >= entry.getKey() + && entry.getKey() > maxRule) { + maxRule = entry.getKey(); + type = entry.getValue(); + duration = punishmentDurations + .getOrDefault( + entry.getKey(), + muteDuration); + } + } + if (type.equals("mute")) { + if (isMuted(player.getName())) + return; + mutedPlayers.put(player.getName(), + System.currentTimeMillis() + + (long) duration + * 1000L); + } + final String fType = type; + final int fDur = duration; + final int fV = violation; + final String fName = player.getName(); + Bukkit.getScheduler().runTask(plugin, + () -> { + Player p = Bukkit.getPlayer(fName); + if (p == null) return; + switch (fType) { + case "warn": + // 警告不处罚,只提示 + break; + case "mute": + p.sendMessage( + msg("chat_muted")); + break; + case "kick": + p.kickPlayer( + msg("chat_muted")); + break; + case "ban": + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban " + fName + + " §c多次发送违规链接"); + break; + case "banip": + InetSocketAddress addr = + p.getAddress(); + if (addr != null) { + String ip = addr + .getAddress() + .getHostAddress(); + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban-ip " + ip + + " §c发送违规链接"); + } + break; + } + }); + } + + public void unmutePlayer(String name) { + mutedPlayers.remove(name); + } + + public void resetPlayer(String name) { + mutedPlayers.remove(name); + violationCount.remove(name); + } + + public boolean isPlayerWhitelisted( + String name) { + return whitelistPlayers.contains(name); + } + + public boolean isNotifyAdmin() { + return notifyAdmin; + } + + public boolean isNotifyAll() { + return notifyAll; + } + + public int getViolationCount(String name) { + return violationCount + .getOrDefault(name, 0); + } + + public void incrementViolation( + String name) { + violationCount.put(name, + getViolationCount(name) + 1); + } + + public List getWhitelistUrls() { + return whitelistUrls; + } + + public Set getWhitelistPlayers() { + return whitelistPlayers; + } + + public Map + getPunishmentRules() { + return punishmentRules; + } + + public Map + getPunishmentDurations() { + return punishmentDurations; + } + + public Map + getMutedPlayers() { + return mutedPlayers; + } + + public void setNotifyAdmin(boolean v) { + notifyAdmin = v; + } + + public void setNotifyAll(boolean v) { + notifyAll = v; + } + + public void setMuteDuration(int v) { + muteDuration = v; + } + + public void addUrl(String url) { + whitelistUrls.add( + url.toLowerCase().trim()); + saveConfig(); + } + + public boolean removeUrl(String url) { + boolean r = whitelistUrls.remove( + url.toLowerCase().trim()); + saveConfig(); + return r; + } + + public void addWhitelistPlayer( + String name) { + whitelistPlayers.add(name); + saveConfig(); + } + + public boolean removeWhitelistPlayer( + String name) { + boolean r = + whitelistPlayers.remove(name); + saveConfig(); + return r; + } + + public void cleanExpired() { + long now = System.currentTimeMillis(); + mutedPlayers.entrySet().removeIf( + e -> e.getValue() <= now); + } + + private boolean parseBool(String v) { + String s = v.toLowerCase().trim(); + return s.equals("true") + || s.equals("on") + || s.equals("yes") + || s.equals("1") + || s.contains("开启") + || s.contains("启用") + || s.contains("是"); + } +} diff --git a/backup/src/main/java/Sdf1_login/ChatInputManager.java b/backup/src/main/java/Sdf1_login/ChatInputManager.java new file mode 100644 index 0000000..49f53fd --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ChatInputManager.java @@ -0,0 +1,842 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class ChatInputManager { + + public enum InputType { + NONE, + CHANGE_PWD_STEP1, + CHANGE_PWD_STEP2, + SET_EMAIL, + SET_EMAIL_VERIFY, + ADMIN_AUTH, + ADMIN_SET_POINTS, + ADMIN_SET_PWD, + ADMIN_DELETE_CONFIRM, + ADMIN_SET_AFK_TIME, + SMTP_CONFIG, + SMTP_VERIFY, + BACK_CHECKIN, + INVITE_INPUT_CODE, + TICKET_TITLE, + TICKET_DESC, + TICKET_SCORE, + TICKET_REJECT, + TICKET_ASSIGN, + TICKET_INVITE_PROVIDER, + TICKET_REMOVE_PROVIDER, + TICKET_COMPLETE, + TICKET_REPORT_ISSUE, + TICKET_REPLY, + TICKET_ADMIN_TYPE, + TICKET_ADMIN_TITLE, + TICKET_ADMIN_DESC, + TICKET_ADMIN_PROVIDER, + BACK_CHECK_ECONOMY_CONFIRM, + USER_MGMT_SEARCH, + + } + + public static class PlayerState { + public InputType type = InputType.NONE; + public String targetPlayer = ""; + public String ticketType = ""; + public String ticketTitle = ""; + public String ticketDesc = ""; + public int ticketId = 0; + public int smtpStep = 0; + public int scoreValue = 0; + + } + + private final Map states = + new HashMap<>(); + private Main plugin; + + public PlayerState getState(Player p) { + return states.computeIfAbsent( + p.getName(), + k -> new PlayerState()); + } + + public void reset(Player p) { + states.remove(p.getName()); + } + public boolean isInFlow(Player p) { + PlayerState state = states.get(p.getName()); + return state != null + && state.type != InputType.NONE; + } + + public void handleInput(Player p, String msg, + Main mainPlugin) { + if (this.plugin == null) + this.plugin = mainPlugin; + PlayerState state = getState(p); + ConfigManager config = + mainPlugin.getConfig2(); + DatabaseManager db = mainPlugin.getDb(); + + switch (state.type) { + + case CHANGE_PWD_STEP1: { + String name = p.getName(); + String salt = (String) db.getField( + name, "password_salt"); + String hash = + PasswordUtils.hash(msg, salt); + boolean mainOk = + db.checkPassword(name, hash); + if (mainOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "main"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + boolean tempOk = + db.checkPasswordOrTemp( + name, hash); + if (tempOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "temp"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + p.sendMessage(config.msg( + "password_wrong")); + return; + } + + case CHANGE_PWD_STEP2: { + String name = p.getName(); + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String oldHash = + (String) db.getField(name, + "password_hash"); + String oldSalt = + (String) db.getField(name, + "password_salt"); + String newSalt = + PasswordUtils.generateSalt(); + String newHash = + PasswordUtils.hash( + msg, newSalt); + if (newHash.equals(oldHash)) { + p.sendMessage(config.msg( + "password_same")); + reset(p); + return; + } + mainPlugin.recordPasswordChange( + name, oldHash, oldSalt); + db.setField(name, + "password_hash", newHash); + db.setField(name, + "password_salt", newSalt); + mainPlugin + .getNeedsPasswordChange() + .remove(name); + p.sendMessage(config.msg( + "password_changed")); + reset(p); + return; + } + + case SET_EMAIL: { + if (!msg.contains("@") + || !msg.contains(".")) { + p.sendMessage(config.msg( + "email_format_error")); + reset(p); + return; + } + String oldEmail = (String) + db.getField(p.getName(), + "email"); + String emailAddr = msg.trim(); + // 已绑定邮箱:验证登录密码 + if (oldEmail != null + && !oldEmail.isEmpty()) { + state.targetPlayer = emailAddr; + state.type = + InputType.SET_EMAIL_VERIFY; + state.ticketTitle = + "need_pwd"; + p.sendMessage( + "§e您已绑定邮箱: " + + oldEmail); + p.sendMessage( + "§e请验证登录密码以确认修改:"); + return; + } + // 未绑定邮箱:直接发验证码 + String code = String.valueOf( + (int) (Math.random() + * 900000 + + 100000)); + state.targetPlayer = emailAddr; + state.ticketTitle = code; + state.type = + InputType.SET_EMAIL_VERIFY; + final String to = emailAddr; + final String c = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + boolean sent = mainPlugin + .getEmail() + .sendVerifyCode( + to, c); + Bukkit.getScheduler() + .runTask(mainPlugin, + () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + reset(fp); + } + }); + }); + return; + } + + + case SET_EMAIL_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§c邮箱设置已取消"); + reset(p); + return; + } + String expected = + state.ticketTitle; + if (msg.trim().equals(expected)) { + db.setField(p.getName(), + "email", + state.targetPlayer); + p.sendMessage( + "§a邮箱已设置: " + + state.targetPlayer); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case ADMIN_AUTH: { + if (msg.equals( + config.adminPassword)) { + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openAdmin(p)); + } else { + p.sendMessage( + "§c管理密码错误"); + } + reset(p); + return; + } + + case ADMIN_SET_POINTS: { + try { + int pts = Integer.parseInt( + msg.trim()); + db.setField( + state.targetPlayer, + "points", pts); + p.sendMessage("§a已将 " + + state.targetPlayer + + " 积分设为 " + pts); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + String tgt = + state.targetPlayer; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openUserDetail( + p, tgt)); + reset(p); + return; + } + + case ADMIN_SET_PWD: { + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String tgt = state.targetPlayer; + String salt = + PasswordUtils + .generateSalt(); + String hash = + PasswordUtils.hash( + msg, salt); + db.setField(tgt, + "temp_password", hash); + db.setField(tgt, "temp_pw_expire", + System.currentTimeMillis() + + 300000L); + db.setField(tgt, + "temp_pw_used", 0); + p.sendMessage("§a已为 " + tgt + + " 设置临时密码"); + Player tp = + Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + tp.kickPlayer( + "§c管理员已设置临时密码," + + "请使用临时密码登录"); + // 邮件通知 + String emailAddr = (String) + db.getField(tgt, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String time = + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()); + String body = "玩家 " + tgt + + ":\n您的密码已于 " + + time + + " 被管理员重置为临时密码。\n" + + "如果是本人操作请忽略。\n" + + "如非本人操作请立即联系管理员。"; + final String to = emailAddr; + final String fBody = body; + final String fName = tgt; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> + mainPlugin.getEmail() + .sendTempPassword( + to, + fName, + fBody)); + } + reset(p); + return; + } + + case ADMIN_DELETE_CONFIRM: { + if (msg.equals( + state.targetPlayer)) { + db.deleteUser( + state.targetPlayer); + p.sendMessage("§a已删除 " + + state.targetPlayer); + Player tgt = + Bukkit.getPlayer( + state.targetPlayer); + if (tgt != null + && tgt.isOnline()) + tgt.kickPlayer( + "§c账号已被删除"); + } else { + p.sendMessage( + "§c输入不匹配,已取消"); + } + reset(p); + return; + } + + case ADMIN_SET_AFK_TIME: { + try { + int minutes = + Integer.parseInt( + msg.trim()); + config.afkTimeout = + minutes * 60; + config.saveSettings(); + p.sendMessage( + "§a挂机时长已设为 " + + minutes + + " 分钟"); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + reset(p); + return; + } + + case SMTP_CONFIG: { + String m = msg.trim(); + if ("000".equals(m) + || "取消取消取消" + .equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if ("取消".equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + int step = state.smtpStep; + if (step <= 1) { + if (!"0".equals(m)) + config.setSmtp( + "smtp地址", m); + state.smtpStep = 2; + p.sendMessage("§e当前端口: " + + config.getSmtp( + "smtp端口")); + p.sendMessage( + "§e输入端口(0跳过):"); + } else if (step == 2) { + if (!"0".equals(m)) + config.setSmtp( + "smtp端口", m); + state.smtpStep = 3; + p.sendMessage( + "§e输入发件邮箱:"); + } else if (step == 3) { + if (!"0".equals(m)) + config.setSmtp( + "smtp账号", m); + state.smtpStep = 4; + p.sendMessage( + "§e输入SMTP密码(0跳过):"); + } else if (step == 4) { + if (!"0".equals(m)) + config.setSmtp( + "smtp密码", m); + state.smtpStep = 5; + p.sendMessage( + "§e输入接收邮箱(留空发到发件邮箱):"); + } else if (step == 5) { + String recv = m; + if (recv.isEmpty() + || "0".equals(recv)) + recv = config.getSmtp( + "smtp账号"); + config.setSmtp( + "验证码接收邮箱", recv); + if (!config + .canSendSmtpVerify()) { + p.sendMessage( + "§c发送间隔不足1分钟"); + reset(p); + return; + } + String code = config + .generateSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String acct = config.getSmtp( + "smtp账号"); + if (acct.isEmpty() + || addr.isEmpty()) { + p.sendMessage( + "§c地址或账号为空"); + reset(p); + return; + } + config.recordSmtpSendTime(); + final String fRecv = recv; + final String fCode = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> fp.sendMessage( + "§e正在发送验证码...")); + boolean sent = + mainPlugin.getEmail() + .sendVerifyCode( + fRecv, + fCode); + final boolean fSent = sent; + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> { + if (fSent) { + state.type = + InputType + .SMTP_VERIFY; + state.smtpStep = 0; + fp.sendMessage( + "§a验证码已发送到 " + + fRecv); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败,查看控制台"); + reset(fp); + } + }); + }); + } + return; + } + + case SMTP_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if (config.checkSmtpVerifyCode( + msg.trim())) { + config.saveSmtp(); + config.clearSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String port = config.getSmtp( + "smtp端口"); + String acct = config.getSmtp( + "smtp账号"); + String recv = config.getSmtp( + "验证码接收邮箱"); + boolean ssl = config.smtpSsl; + p.sendMessage("§a§l=== SMTP配置完成 ==="); + p.sendMessage("§7地址: §f" + addr); + p.sendMessage("§7端口: §f" + port); + p.sendMessage("§7账号: §f" + acct); + p.sendMessage("§7接收: §f" + recv); + p.sendMessage("§7加密: §f" + ssl); + p.sendMessage("§a§l===================="); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case BACK_CHECKIN: { + String result = + mainPlugin.getCheckIn() + .backCheckIn(p, msg); + if (result == null) { + // 等待选择(NEED_CONFIRM) + return; + } + if (result.startsWith( + "NEED_CONFIRM:")) { + String[] parts = + result.split(":"); + // 存储到Main的Map + mainPlugin.pendingBackCheck.put( + p.getName(), + parts[1] + ":" + parts[2]); + return; + } + if (result.startsWith("§c")) { + p.sendMessage(result); + } else { + p.sendMessage(result); + } + reset(p); + return; + } + + + case INVITE_INPUT_CODE: { + String code = msg.trim(); + String myName = p.getName(); + String myCode = + (String) db.getField( + myName, + "invite_code"); + if (code.isEmpty()) { + p.sendMessage( + "§c邀请码不能为空"); + reset(p); + return; + } + if (code.equals(myCode)) { + p.sendMessage( + "§c不能用自己邀请码"); + reset(p); + return; + } + boolean found = false; + for (Map u : + db.getAllUsers()) { + String uc = (String) + u.getOrDefault( + "invite_code", ""); + if (code.equals(uc)) { + String inv = (String) + u.get("player_name"); + db.setField(myName, + "invited_by", inv); + db.addPoints(inv, 50); + p.sendMessage( + "§a邀请成功!邀请人: " + + inv); + Player ip = + Bukkit.getPlayer(inv); + if (ip != null + && ip.isOnline()) + ip.sendMessage("§a" + + myName + + " 使用了您的邀请码"); + found = true; + break; + } + } + if (!found) + p.sendMessage( + "§c邀请码不存在"); + reset(p); + return; + } + + case TICKET_TITLE: { + state.ticketTitle = msg; + state.type = + InputType.TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return; + } + + case TICKET_DESC: { + String tt = state.ticketType; + String tl = state.ticketTitle; + reset(p); + mainPlugin.getTicket() + .submitTicket( + p, tt, tl, msg); + return; + } + + case TICKET_SCORE: { + int tid = state.ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + reset(p); + mainPlugin.getTicket() + .confirmAndScore( + p, tid, sc); + } catch ( + NumberFormatException e) { + reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return; + } + + case TICKET_REJECT: { + int tid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .rejectTicket( + p, tid, msg); + return; + } + + case TICKET_ASSIGN: { + int tid = state.ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + reset(p); + mainPlugin.getTicket() + .assignToProvider( + p, tid, prov, + price); + final int fTid = tid; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getTicket() + .openDetail( + p, fTid)); + return; + } + + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return; + } + db.addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return; + } + + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.isServiceProvider(tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return; + } + db.removeServiceProvider(tgt); + p.sendMessage("§a已移除 " + tgt); + return; + } + + case TICKET_COMPLETE: { + try { + int tid = Integer.parseInt( + msg.trim()); + mainPlugin.getTicket() + .completeAsProvider( + p, tid); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效工单号"); + } + reset(p); + return; + } + + case TICKET_REPORT_ISSUE: { + String report = msg.trim(); + reset(p); + p.sendMessage("§e异常已上报"); + String tag = config.adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§c§l[工单异常] §f" + + p.getName() + + ": " + report); + } + } + return; + } + + case TICKET_REPLY: { + int replyTid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .replyTicket( + p, replyTid, msg); + return; + } + + case TICKET_ADMIN_TYPE: { + state.ticketType = msg.trim(); + state.type = + InputType + .TICKET_ADMIN_TITLE; + p.sendMessage( + "§e请输入工单标题:"); + return; + } + + case TICKET_ADMIN_TITLE: { + state.ticketTitle = msg; + state.type = + InputType + .TICKET_ADMIN_DESC; + p.sendMessage( + "§e请输入工单描述:"); + return; + } + + case TICKET_ADMIN_DESC: { + state.targetPlayer = msg; + state.type = + InputType + .TICKET_ADMIN_PROVIDER; + p.sendMessage( + "§e输入服务商名和价格:"); + p.sendMessage( + "§7例: playerA 20"); + return; + } + + case TICKET_ADMIN_PROVIDER: { + String[] parts2 = msg.trim() + .split("\\s+"); + String prov2 = parts2[0]; + int price2 = 0; + if (parts2.length >= 2) { + try { + price2 = Integer.parseInt( + parts2[1]); + } catch ( + NumberFormatException e) { + } + } + String t2 = state.ticketType; + String ti2 = state.ticketTitle; + String d2 = state.targetPlayer; + reset(p); + mainPlugin.getTicket() + .adminCreateAndAssign( + p, t2, ti2, + d2, prov2, price2); + return; + } + + default: + reset(p); + return; + } + } +} diff --git a/backup/src/main/java/Sdf1_login/CheckInManager.java b/backup/src/main/java/Sdf1_login/CheckInManager.java new file mode 100644 index 0000000..b594b0f --- /dev/null +++ b/backup/src/main/java/Sdf1_login/CheckInManager.java @@ -0,0 +1,302 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class CheckInManager { + + private final Main plugin; + + public CheckInManager(Main plugin) { + this.plugin = plugin; + } + + // ===== 今日是否已签到 ===== + public boolean isCheckedInToday( + String name) { + String last = (String) plugin.getDb() + .getField(name, + "last_checkin_date"); + String today = new SimpleDateFormat( + "yyyy-MM-dd") + .format(new Date()); + return today.equals(last); + } + + // ===== 签到 ===== + // ===== 签到 ===== + public String checkIn(Player p) { + String name = p.getName(); + if (isCheckedInToday(name)) { + return plugin.getConfig2() + .msg("checkin_already"); + } + + Object streakObj = plugin.getDb() + .getField(name, "checkin_streak"); + int streak = streakObj != null + ? ((Number) streakObj).intValue() + : 0; + + String lastDate = (String) plugin.getDb() + .getField(name, "last_checkin_date"); + String today = new SimpleDateFormat("yyyy-MM-dd") + .format(new Date()); + + if (lastDate != null && !lastDate.isEmpty()) { + try { + java.util.Date lastD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(lastDate); + java.util.Date todayD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(today); + long diff = todayD.getTime() + - lastD.getTime(); + if (diff > 86400000L * 2) { + streak = 0; + } else if (diff > 86400000L) { + streak++; + } + } catch (Exception ignored) { + } + } + + if (streak == 0) streak = 1; + else streak++; + + double multi = 1.0; + if (streak >= 21) multi = 1.5; + else if (streak >= 14) multi = 1.3; + else if (streak >= 7) multi = 1.1; + + int basePoints = + plugin.getConfig2().checkinPoints; + int points = (int) (basePoints * multi); + + plugin.getDb().addPoints(name, points); + plugin.getDb().setField(name, + "checkin_streak", streak); + plugin.getDb().setField(name, + "last_checkin_date", today); + + Object totalObj = plugin.getDb() + .getField(name, "total_checkin_days"); + int total = totalObj != null + ? ((Number) totalObj).intValue() + : 0; + plugin.getDb().setField(name, + "total_checkin_days", total + 1); + + // ★ 债券盲盒奖励(6参数版,写入流水)★ + int bondMin = + plugin.getConfig2().checkinBondMin; + int bondMax = + plugin.getConfig2().checkinBondMax; + if (bondMin <= 0) bondMin = 1; + if (bondMax < bondMin) bondMax = bondMin; + int bondReward = bondMin + + new java.util.Random() + .nextInt(bondMax - bondMin + 1); + plugin.getBonds().addBonds(name, bondReward, + "daily_sign", "", "签到系统", + "每日签到连续" + streak + "天"); + + return plugin.getConfig2() + .msg("checkin_success") + .replace("{points}", + String.valueOf(points)) + .replace("{streak}", + String.valueOf(streak)) + .replace("{multi}", + String.valueOf(multi)) + + " §e+" + bondReward + "债券"; + } + + // ===== 获取连续天数 ===== + public int getStreak(String name) { + Object val = plugin.getDb() + .getField(name, + "checkin_streak"); + if (val instanceof Number) { + return ((Number) val) + .intValue(); + } + return 0; + } + // 标准化日期 + private String normalizeDate(String input) { + String s = input.trim().replace("/", "-"); + if (s.matches("\\d{8}")) { + return s.substring(0, 4) + "-" + + s.substring(4, 6) + "-" + + s.substring(6, 8); + } + return s; + } + // ===== 补签入口 ===== + public String backCheckIn( + Player p, String dateStr) { + String name = p.getName(); + String normalized = + normalizeDate(dateStr); + + // 严格日期验证 + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd"); + sdf.setLenient(false); + java.util.Date target; + try { + target = sdf.parse(normalized); + } catch (Exception ex) { + return "§c无效日期: " + dateStr; + } + String reformat = sdf.format(target); + if (!reformat.equals(normalized)) { + return "§c无效日期: " + dateStr; + } + + String today = sdf.format(new Date()); + if (normalized.compareTo(today) >= 0) { + return "§c今天不需要补签"; + } + + String threeDaysAgo = getPastDate(3); + if (normalized.compareTo( + threeDaysAgo) < 0) { + return "§c只能补签3天内" + + "(最多到 " + threeDaysAgo + ")"; + } + + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + boolean alreadyBacked = false; + if (!existing.isEmpty()) { + for (String d : existing.split(",")) { + if (normalizeDate(d.trim()) + .equals(normalized)) { + alreadyBacked = true; + break; + } + } + } + if (alreadyBacked) { + return "§c该日期已补签: " + normalized; + } + + Object normalCheck = + plugin.getDb().getField( + name, "last_checkin_date"); + String lastCheckin = normalCheck != null + ? normalCheck.toString() : ""; + if (lastCheckin.equals(normalized)) { + return "§c该日期已签到: " + normalized; + } + + // ★ 补签消耗:3债券 + 10积分 + // 积分不够时:用 9债券(3倍)代替 ★ + int bondCost = 3; + int pointCost = 10; + int bondAltCost = 9; // 3倍债券替代 + + int myBonds = plugin.getBonds() + .getBonds(name); + Object ptsObj = plugin.getDb() + .getField(name, "points"); + int myPoints = ptsObj != null + ? ((Number) ptsObj).intValue() + : 0; + + + // 方案A:3债券 + 10积分 + boolean canA = myBonds >= bondCost + && myPoints >= pointCost; + // 方案B:9债券(积分不够时) + boolean canB = myBonds >= bondAltCost + && myPoints < pointCost; + + if (!canA && !canB) { + if (myPoints < pointCost + && myBonds < bondAltCost) { + return "§c补签费用不足" + + "(需3债券+10积分," + + "或9债券代替)" + + " 当前: " + + myBonds + "债券 " + + myPoints + "积分"; + } + if (myBonds < bondCost) { + return "§c债券不足(需" + + bondCost + "枚)"; + } + return "§c积分不足(需" + + pointCost + "分)"; + } + + // 执行扣款 + if (canA) { + // 方案A:3债券 + 10积分 + plugin.getBonds().deductBonds( + name, bondCost, + "admin_deduct", "", + "补签系统", "补签扣款"); + plugin.getDb().deductPoints( + name, pointCost); + } else { + // 方案B:9债券代替 + plugin.getBonds().deductBonds( + name, bondAltCost, + "admin_deduct", "", + "补签系统", "补签扣款(债券替代)"); + } + + doBackCheckIn(p, normalized); + + String costMsg = canA + ? bondCost + "债券+" + pointCost + "积分" + : bondAltCost + "债券(替代)"; + return "§a补签成功!日期: " + + normalized + + " §7消耗: " + costMsg; + } + + + + // ===== 执行补签 ===== + // ===== 执行补签 ===== + public void doBackCheckIn( + Player p, String dateStr) { + String name = p.getName(); + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + String updated = existing.isEmpty() + ? dateStr + : existing + "," + dateStr; + plugin.getDb().setField(name, + "backcheck_dates", updated); + int streak = getStreak(name); + plugin.getDb().setField(name, + "checkin_streak", streak + 1); + // ★ 补签不发放奖励,只记录 + } + + // ===== 获取N天前日期 ===== + private String getPastDate( + int daysAgo) { + Calendar cal = + Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, + -daysAgo); + return new SimpleDateFormat( + "yyyy-MM-dd") + .format(cal.getTime()); + } +} \ No newline at end of file diff --git a/backup/src/main/java/Sdf1_login/CommissionManager.java b/backup/src/main/java/Sdf1_login/CommissionManager.java new file mode 100644 index 0000000..cb24687 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/CommissionManager.java @@ -0,0 +1,227 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.logging.Logger; + +public class CommissionManager { + + private final Main main; + private final Logger logger; + private final List rules = + new ArrayList<>(); + + public CommissionManager(Main main) { + this.main = main; + this.logger = main.getLogger(); + loadRules(); + } + + public void loadRules() { + rules.clear(); + File f = new File( + main.getDataFolder(), + "提成规则.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader br = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) + continue; + // 格式: 邀请人数:提成百分比 + // 如 "5:15" = 邀请5人提成15% + String[] parts = + line.split(":"); + if (parts.length >= 2) { + try { + int count = Integer.parseInt( + parts[0].trim()); + double rate = Double + .parseDouble( + parts[1] + .trim()) / 100.0; + rules.add(new double[]{ + count, rate}); + } catch (Exception ignored) { + } + } + } + // 按人数排序(从大到小) + rules.sort((a, b) -> + Double.compare(b[0], a[0])); + logger.info( + "[Commission] 加载 " + + rules.size() + " 条提成规则"); + } catch (Exception e) { + logger.warning( + "[Commission] 加载失败: " + + e.getMessage()); + } + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 邀请提成规则"); + pw.println("# 格式: 邀请人数:提成百分比"); + pw.println("# 邀请10人提成20%"); + pw.println("10:20"); + pw.println("# 邀请5人提成15%"); + pw.println("5:15"); + pw.println("# 邀请1人提成10%"); + pw.println("1:10"); + } catch (IOException ignored) { + } + } + + /** + * 获取邀请人的提成比例 + * 根据已邀请人数匹配最高规则 + */ + public double getCommissionRate( + String inviter) { + int inviteCount = getInviteCount( + inviter); + for (double[] rule : rules) { + if (inviteCount >= (int) rule[0]) { + return rule[1]; + } + } + return 0; + } + + /** + * 获取邀请人数 + */ + public int getInviteCount( + String inviter) { + try { + Object val = + main.getDb().getField( + inviter, + "monthly_invite_count"); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + Object total = + main.getDb().getField( + inviter, + "total_invite_count"); + if (total instanceof Number) { + return ((Number) total).intValue(); + } + } catch (Exception ignored) { + } + return 0; + } + + /** + * 发放提成 + * 被邀请人获得奖励时,邀请人获得提成 + */ + public void payCommission( + String invitee, + double originalAmount) { + // 查找邀请人 + String inviter = getInviter(invitee); + if (inviter == null + || inviter.isEmpty()) return; + + double rate = getCommissionRate( + inviter); + if (rate <= 0) return; + + double commission = + originalAmount * rate; + if (commission <= 0) return; + + // 通过 Vault 发放 + var rsp = main.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy.Economy.class); + if (rsp == null) return; + net.milkbowl.vault.economy.Economy econ = + rsp.getProvider(); + if (econ == null) return; + + econ.depositPlayer(inviter, commission); + + // 通知邀请人 + Player inviterP = + main.getServer() + .getPlayer(inviter); + if (inviterP != null + && inviterP.isOnline()) { + inviterP.sendMessage( + "§a§l[邀请提成] §f" + + invitee + " 完成任务," + + "你获得 §e" + + String.format("%.2f", + commission) + + " §f游戏币提成 (" + + (int)(rate * 100) + "%)"); + } + + logger.info( + "[Commission] 提成: " + + inviter + " ← " + + invitee + " | " + + String.format("%.2f", + originalAmount) + + " × " + + (int)(rate * 100) + "% = " + + String.format("%.2f", + commission)); + } + + /** + * 获取邀请人 + */ + private String getInviter(String player) { + try { + Object val = + main.getDb().getField( + player, + "invited_by"); + if (val != null + && !val.toString() + .isEmpty()) { + return val.toString(); + } + } catch (Exception ignored) { + } + return null; + } + + /** + * 判断金额是否包含经济内容 + */ + public boolean hasEconomyContent( + String text) { + String t = text.toLowerCase(); + return t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额"); + } +} diff --git a/backup/src/main/java/Sdf1_login/ConfigManager.java b/backup/src/main/java/Sdf1_login/ConfigManager.java new file mode 100644 index 0000000..a15ada0 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ConfigManager.java @@ -0,0 +1,372 @@ +package Sdf1_login; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class ConfigManager { + + private final File dataFolder; + private final Map messages + = new LinkedHashMap<>(); + private final Map smtpSettings + = new LinkedHashMap<>(); + + // ===== 公共字段 ===== + public String adminTag = "admin"; + public String adminPassword = "qweasd"; + public int maxAccountsPerIP = 3; + public String approvalMode = "manual"; + public int autoApproveDelayMinutes = 30; + public int loginTimeout = 60; + public boolean afkEnabled = false; + public int afkTimeout = 300; + public boolean smtpSsl = true; + public String smtpVerifyCode = ""; + public long smtpVerifyExpire = 0; + public long smtpLastSendTime = 0; + public String smtpConfiguringPlayer = ""; + public String broadcastServerUrl = ""; + public int httpPort = 8080; + public String garbageTag = "admin"; + + public double checkinRewardFixed = 0; + public double checkinRewardMin = 0; + public double checkinRewardMax = 0; + public boolean checkinGivePoints = true; + public int checkinPoints = 10; + public String checkinRewardType = "none"; + public int backCheckPointMultiplier = 5; + public int checkinBondMin = 1; + public int checkinBondMax = 3; + + public double providerPointsPerScore = 2.0; + public int baseEconomyReward = 100; + public double providerEconomyPerScore = 50.0; + public int requesterPointsReward = 10; + + public boolean garbageEnabled = true; + public int garbageInterval = 300; + public int garbageMaxRounds = 1; + + // ★ 构造函数接收 File,和 Main.java 调用一致 ★ + public ConfigManager(File dataFolder) { + this.dataFolder = dataFolder; + } + public String rewardChannel = "债券"; // bonds = 债券,economy = Vault经济 + + + // ==================== 消息 ==================== + + public void loadMessages() { + messages.clear(); + File file = new File(dataFolder, "消息.txt"); + if (!file.exists()) createDefaultMessages(file); + loadMap(file, messages); + } + + public String msg(String key) { + String val = messages.getOrDefault(key, key); + val = val.replace("", "&l"); + val = val.replace("", "&r"); + val = val.replace("
    ", "\n"); + val = val.replace("\\n", "\n"); + val = val.replaceAll( + "&([0-9a-fk-orA-FK-OR])", "§$1"); + return val; + } + + private void createDefaultMessages(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 消息配置 ====="); + L.add("login_timeout=§c登录超时"); + L.add("not_registered=§e您尚未注册"); + L.add("not_logged_in=§e您尚未登录"); + L.add("already_registered=§c您已注册过"); + L.add("already_logged_in=§c您已登录"); + L.add("reg_success=§a注册成功!"); + L.add("login_success=§a登录成功!"); + L.add("login_failed=§c密码错误"); + L.add("password_wrong=§c密码错误"); + L.add("password_changed=§a密码修改成功!"); + L.add("password_format_error=§c密码需6位以上含大小写和数字"); + L.add("password_same=§c新密码不能与旧密码相同"); + L.add("checkin_already=§c您今日已签到"); + L.add("checkin_success=§a签到成功!获得 {points} 积分"); + L.add("chat_muted=§c你已被禁言"); + L.add("未知参数=§c未知参数或权限不足"); + writeLines(f, L); + } + + // ==================== SMTP ==================== + + public void loadSmtp() { + smtpSettings.clear(); + File file = new File(dataFolder, "SMTP设置.txt"); + if (!file.exists()) createDefaultSmtp(file); + loadMap(file, smtpSettings); + } + + public String getSmtp(String key) { + return smtpSettings.getOrDefault(key, ""); + } + + public void setSmtp(String key, String value) { + smtpSettings.put(key, value); + } + + public void saveSmtp() { + File file = new File(dataFolder, "SMTP设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + for (Map.Entry e + : smtpSettings.entrySet()) { + L.add(e.getKey() + "=" + e.getValue()); + } + writeLines(file, L); + } + + private void createDefaultSmtp(File f) { + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + L.add("smtp地址=smtp.example.com"); + L.add("smtp端口=465"); + L.add("smtp账号="); + L.add("smtp密码="); + L.add("发件人名称=Sdf1_login"); + L.add("smtp加密=true"); + writeLines(f, L); + } + + // ==================== 插件设置 ==================== + + public void loadSettings() { + File file = new File(dataFolder, "插件设置.txt"); + if (!file.exists()) createDefaultSettings(file); + Map m = new LinkedHashMap<>(); + loadMap(file, m); + + adminTag = m.getOrDefault("管理标签", adminTag); + adminPassword = m.getOrDefault("管理密码", adminPassword); + maxAccountsPerIP = parseInt(m.getOrDefault("每IP最大账号数", "3"), 3); + approvalMode = m.getOrDefault("审批模式", approvalMode); + autoApproveDelayMinutes = parseInt(m.getOrDefault("自动审批延迟分钟", "30"), 30); + loginTimeout = parseInt(m.getOrDefault("登录超时秒数", "60"), 60); + afkEnabled = Boolean.parseBoolean(m.getOrDefault("挂机踢出", "false")); + afkTimeout = parseInt(m.getOrDefault("挂机超时秒数", "300"), 300); + smtpSsl = Boolean.parseBoolean(m.getOrDefault("smtp加密", "true")); + httpPort = parseInt(m.getOrDefault("http-port", "8080"), 8080); + broadcastServerUrl = m.getOrDefault("resource-pack-url", ""); + + providerPointsPerScore = parseDouble(m.getOrDefault("服务商积分倍率", "2.0"), 2.0); + baseEconomyReward = parseInt(m.getOrDefault("基础经济奖励", "100"), 100); + providerEconomyPerScore = parseDouble(m.getOrDefault("经济奖励每评分", "50.0"), 50.0); + requesterPointsReward = parseInt(m.getOrDefault("报单人积分奖励", "10"), 10); + + garbageEnabled = Boolean.parseBoolean(m.getOrDefault("垃圾站_启用", "true")); + garbageInterval = parseInt(m.getOrDefault("垃圾站_清理间隔秒", "300"), 300); + garbageMaxRounds = parseInt(m.getOrDefault("垃圾站_保留轮数", "1"), 1); + + checkinRewardFixed = parseDouble(m.getOrDefault("签到固定金额", "0"), 0); + checkinRewardMin = parseDouble(m.getOrDefault("签到最小金额", "0"), 0); + checkinRewardMax = parseDouble(m.getOrDefault("签到最大金额", "0"), 0); + checkinGivePoints = Boolean.parseBoolean(m.getOrDefault("签到给积分", "true")); + checkinPoints = parseInt(m.getOrDefault("签到积分数量", "10"), 10); + backCheckPointMultiplier = parseInt(m.getOrDefault("补签积分倍率", "5"), 5); + checkinBondMin = parseInt(m.getOrDefault("签到债券最小", "1"), 1); + checkinBondMax = parseInt(m.getOrDefault("签到债券最大", "3"), 3); + if (checkinBondMax < checkinBondMin) checkinBondMax = checkinBondMin; + + if (checkinRewardFixed > 0) checkinRewardType = "fixed"; + else if (checkinRewardMin > 0 && checkinRewardMax > checkinRewardMin) checkinRewardType = "range"; + else checkinRewardType = "none"; + rewardChannel = m.getOrDefault("奖励发放方式", "债券"); + rewardChannel = normalizeRewardChannel(rewardChannel); + +// 验证合法值 + if (!"economy".equalsIgnoreCase(rewardChannel) + && !"bonds".equalsIgnoreCase(rewardChannel)) { + rewardChannel = "bonds"; + } + + + + // ★ 自动补充新配置项 ★ + boolean needAppend = false; + List newKeys = new ArrayList<>(); + if (!m.containsKey("签到债券最小")) { needAppend = true; newKeys.add("签到债券最小=" + checkinBondMin); } + if (!m.containsKey("签到债券最大")) { needAppend = true; newKeys.add("签到债券最大=" + checkinBondMax); } + if (!m.containsKey("奖励发放方式")) { + needAppend = true; + newKeys.add("奖励发放方式=债券"); + } + if (needAppend) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + for (String key : newKeys) { bw.write(key); bw.newLine(); } + bw.flush(); bw.close(); + System.out.println("[Config] 已补充签到债券配置"); + + } catch (IOException ignored) {} + if (!m.containsKey("奖励发放方式")) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + bw.write("奖励发放方式=bonds"); + bw.newLine(); + bw.flush(); bw.close(); + } catch (IOException ignored) {} + } + + } + } + /** + * 奖励方式标准化:中英文全兼容 + * "债券" "bonds" "Bond" "BONDS" → "bonds" + * "经济" "economy" "Economy" "ECONOMY" → "economy" + * 其他任何值 → "bonds"(默认债券) + */ + private String normalizeRewardChannel(String raw) { + if (raw == null) return "bonds"; + String s = raw.trim().toLowerCase(); + // 中文 + if (s.contains("债券")) return "bonds"; + if (s.contains("经济")) return "economy"; + // 英文 + if (s.contains("bond")) return "bonds"; + if (s.contains("econ")) return "economy"; + if (s.contains("Vault")) return "economy"; + // 兜底 + return "bonds"; + } + + public void saveSettings() { + File file = new File(dataFolder, "插件设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=" + adminTag); + L.add("管理密码=" + adminPassword); + L.add("每IP最大账号数=" + maxAccountsPerIP); + L.add("审批模式=" + approvalMode); + L.add("自动审批延迟分钟=" + autoApproveDelayMinutes); + L.add("登录超时秒数=" + loginTimeout); + L.add("挂机踢出=" + afkEnabled); + L.add("挂机超时秒数=" + afkTimeout); + L.add("resource-pack-url=" + broadcastServerUrl); + L.add("http-port=" + httpPort); + L.add("垃圾站_启用=" + garbageEnabled); + L.add("垃圾站_清理间隔秒=" + garbageInterval); + L.add("垃圾站_保留轮数=" + garbageMaxRounds); + L.add("签到固定金额=" + checkinRewardFixed); + L.add("签到最小金额=" + checkinRewardMin); + L.add("签到最大金额=" + checkinRewardMax); + L.add("签到给积分=" + checkinGivePoints); + L.add("签到积分数量=" + checkinPoints); + L.add("签到债券最小=" + checkinBondMin); + L.add("签到债券最大=" + checkinBondMax); + L.add("补签积分倍率=" + backCheckPointMultiplier); + L.add("奖励发放方式=债券"); + writeLines(file, L); + } + + private void createDefaultSettings(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=admin"); + L.add("管理密码=qweasd"); + L.add("每IP最大账号数=3"); + L.add("审批模式=manual"); + L.add("自动审批延迟分钟=30"); + L.add("登录超时秒数=60"); + L.add("挂机踢出=false"); + L.add("挂机超时秒数=300"); + L.add("resource-pack-url="); + L.add("http-port=8080"); + L.add("垃圾站_启用=true"); + L.add("垃圾站_清理间隔秒=300"); + L.add("垃圾站_保留轮数=1"); + L.add("签到固定金额=0"); + L.add("签到最小金额=50"); + L.add("签到最大金额=500"); + L.add("签到给积分=true"); + L.add("签到积分数量=10"); + L.add("签到债券最小=1"); + L.add("签到债券最大=3"); + L.add("补签积分倍率=5"); + L.add("奖励发放方式=债券"); + writeLines(f, L); + } + public List getAfkWhitelist() { + List list = new ArrayList<>(); + File file = new File(dataFolder, "挂机白名单.txt"); + if (!file.exists()) { + try { + file.createNewFile(); + FileWriter fw = new FileWriter(file); + fw.write("# 每行一个玩家名\n"); + fw.flush(); + fw.close(); + } catch (IOException ignored) {} + return list; + } + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(file), + StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) + list.add(line); + } + } catch (IOException ignored) {} + return list; + } + + // ==================== 其他 ==================== + + public String generateSmtpVerifyCode() { + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(); + java.util.Random r = new java.util.Random(); + for (int i = 0; i < 6; i++) sb.append(chars.charAt(r.nextInt(chars.length()))); + smtpVerifyCode = sb.toString(); + smtpVerifyExpire = System.currentTimeMillis() + 300000L; + return smtpVerifyCode; + } + + public boolean checkSmtpVerifyCode(String code) { + if (System.currentTimeMillis() > smtpVerifyExpire) { smtpVerifyCode = ""; return false; } + return smtpVerifyCode.equalsIgnoreCase(code); + } + + public void clearSmtpVerifyCode() { smtpVerifyCode = ""; smtpVerifyExpire = 0; } + public boolean canSendSmtpVerify() { return System.currentTimeMillis() - smtpLastSendTime >= 60000L; } + public void recordSmtpSendTime() { smtpLastSendTime = System.currentTimeMillis(); } + + // ==================== 文件工具 ==================== + + private void loadMap(File file, Map map) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq > 0) map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); + } + } catch (IOException ignored) {} + } + + private void writeLines(File file, List lines) { + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + for (String l : lines) { w.write(l); w.newLine(); } + } catch (IOException ignored) {} + } + + private double parseDouble(String s, double def) { try { return Double.parseDouble(s.trim()); } catch (Exception e) { return def; } } + private int parseInt(String s, int def) { try { return Integer.parseInt(s.trim()); } catch (Exception e) { return def; } } +} diff --git a/backup/src/main/java/Sdf1_login/CypayCommand.java b/backup/src/main/java/Sdf1_login/CypayCommand.java new file mode 100644 index 0000000..7fd9067 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/CypayCommand.java @@ -0,0 +1,577 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +public class CypayCommand + implements CommandExecutor, TabCompleter { + + private final Main plugin; + private static final SimpleDateFormat SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + public CypayCommand(Main plugin) { + this.plugin = plugin; + } + + private boolean hasAdminPerm(CommandSender s) { + if (s.isOp()) return true; + if (!(s instanceof Player)) return true; + Player p = (Player) s; + String tag = + plugin.getConfig2().adminTag; + return !tag.isEmpty() + && p.getScoreboardTags().contains(tag); + } + + private int parseAmount(String s) { + return Integer.parseInt( + s.trim().replaceAll("[+]", "")); + } + + private List filter( + List list, String prefix) { + String low = prefix.toLowerCase(); + return list.stream() + .filter(x -> x.toLowerCase() + .startsWith(low)) + .collect(Collectors.toList()); + } + + @Override + public boolean onCommand(CommandSender sender, + Command cmd, + String label, + String[] args) { + + // ===== /cypay — 查自己 ===== + if (args.length == 0) { + String name = sender instanceof Player + ? sender.getName() : "Console"; + int bonds = + plugin.getBonds().getBonds(name); + String status = plugin.getBonds() + .getAccountStatus(name); + sender.sendMessage( + "§e§l[债券] §f" + name + + " 余额: §a" + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals(status) + ? "§c冻结中" + : "§a正常")); + return true; + } + + // ===== /cypay import ===== + /* if (args.length == 2 + && "import".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String file = args[1]; + if (!file.endsWith(".txt")) + file += ".txt"; + int result = plugin.getCDK() + .importFromFile(file); + if (result == -1) + sender.sendMessage("§c文件不存在: " + + file); + else + sender.sendMessage("§a导入完成: " + + result + " 条"); + return true; + }*/ + + // ===== /cypay freeze ===== + if (args.length == 2 + && "freeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (plugin.getBonds().isFrozen(target)) { + sender.sendMessage("§c" + target + + " 已处于冻结状态"); + return true; + } + plugin.getBonds().freezeAccount(target); + sender.sendMessage("§a已冻结 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§c§l[系统] 你的债券账户" + + "已被管理员冻结"); + return true; + } + + // ===== /cypay unfreeze ===== + if (args.length == 2 + && "unfreeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (!plugin.getBonds() + .isFrozen(target)) { + sender.sendMessage("§c" + target + + " 未被冻结"); + return true; + } + plugin.getBonds().unfreezeAccount(target); + sender.sendMessage("§a已解冻 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a§l[系统] 你的债券账户" + + "已被解冻"); + return true; + } + + // ===== /cypay water [days] ===== + if (args.length >= 2 + && "water".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + int days = 7; + if (args.length >= 3) { + try { + days = Integer.parseInt(args[2]); + } catch (NumberFormatException ignored) { + } + } + days = Math.min(days, + BondManager.ADMIN_MAX_WATER_DAYS); + + if (days <= 0) days = 1; + + List> txs = + plugin.getBonds() + .getTransactions( + target, days); + + sender.sendMessage( + "§e§l===== " + target + + " 流水记录 (近" + + days + "天) ====="); + sender.sendMessage("§f当前余额: §a" + + plugin.getBonds() + .getBonds(target) + + " §f状态: " + + (plugin.getBonds() + .isFrozen(target) + ? "§c冻结" : "§a正常")); + + if (txs.isEmpty()) { + sender.sendMessage("§7 (暂无记录)"); + } else { + for (Map tx : txs) { + sender.sendMessage( + formatTxLine(tx)); + } + } + sender.sendMessage( + "§e§l==========================" + + "=========="); + return true; + } + // ===== /cypay printer [player] ===== + if ("printer".equalsIgnoreCase(args[0])) { + if (!hasAdminPerm(sender) + && !(sender instanceof Player) + == false) { + // 控制台也可以用 + if (!(sender instanceof Player)) + ; // 放行 + else if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + } + if (args.length == 1) { + // 打印全服 + sender.sendMessage( + "§e正在导出全服流水..."); + plugin.getBonds(); // 确保实例就绪 + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printAll(sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + if (args.length == 2) { + // 打印指定玩家 + String target = args[1]; + sender.sendMessage("§e正在导出 " + + target + " 的流水..."); + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printPlayer(target, sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + } + + // ===== /cypay give/remove ===== + if (args.length == 3) { + String action = args[0].toLowerCase(); + if ("give".equals(action) + || "remove".equals(action)) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + try { + int amount = + parseAmount(args[2]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + String txType = + "give".equals(action) + ? BondManager + .TX_ADMIN_GIVE + : BondManager + .TX_ADMIN_DEDUCT; + String opName = + sender instanceof Player + ? sender.getName() + : "Console"; + + if ("give".equals(action)) { + plugin.getBonds().addBonds( + target, amount, + txType, "", + opName, "管理员发放"); + sender.sendMessage( + "§a已给 " + target + + " " + amount + + " 债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a管理员给了你 " + + amount + + " 债券"); + } else { + if (plugin.getBonds() + .deductBonds(target, + amount, + txType, "", + opName, + "管理员扣除")) { + sender.sendMessage( + "§a已扣除 " + target + + " " + amount + + " 债券"); + Player tp = plugin + .getServer() + .getPlayerExact( + target); + if (tp != null) + tp.sendMessage( + "§c管理员扣除了" + + "你的 " + + amount + + " 债券"); + } else { + sender.sendMessage( + "§c" + target + + " 债券不足" + + "或账户冻结"); + } + } + } catch (NumberFormatException e) { + sender.sendMessage( + "§c金额格式错误"); + } + return true; + } + } + + // ===== /cypay — 仅玩家转账 ===== + if (args.length == 2) { + try { + int amount = parseAmount(args[1]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + // ★ 控制台禁止转账 + if (!(sender instanceof Player)) { + sender.sendMessage( + "§c控制台不能转账,请使用 " + + "/cypay give <玩家> <金额>"); + return true; + } + Player p = (Player) sender; + String target = args[0]; + BondManager.TransferResult r = + plugin.getBonds().transfer( + p.getName(), + target, amount); + if (r.success) { + p.sendMessage("§e§l[债券转账] §f向 §a" + + target + " §f转出 §c" + + amount + " §f枚债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage("§e§l[债券转账] §f收到 §a" + + p.getName() + + " §f转入 §a" + + amount + " §f枚债券"); + } else { + p.sendMessage("§c" + r.message + + ",当前: " + + plugin.getBonds() + .getBonds(p.getName())); + } + return true; + } catch (NumberFormatException ignored) { + } + } + + // ===== /cypay — 查指定玩家 ===== + if (args.length == 1) { + String target = args[0]; + // ★ 验证玩家在 login.db 中存在 ★ + if (!plugin.getDb().userExists(target)) { + sender.sendMessage("§c玩家 " + target + + " 不存在于服务器"); + return true; + } + + // String target = args[0]; + if (hasAdminPerm(sender)) { + int bonds = plugin.getBonds() + .getBonds(target); + String status = plugin.getBonds() + .getAccountStatus(target); + sender.sendMessage( + "§e§l[债券] §f" + target + + " 余额: §a" + + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals( + status) + ? "§c冻结中" + : "§a正常")); + } else { + int bonds = plugin.getBonds() + .getBonds( + sender.getName()); + sender.sendMessage( + "§e§l[债券] §f你的余额: §a" + + bonds); + } + return true; + } + + showHelp(sender); + return true; + } + + // ===== 流水行格式化 ===== + + private String formatTxLine( + Map tx) { + String type = (String) tx.get("type"); + int amount = ((Number) tx.get("amount")) + .intValue(); + String tgt = (String) tx.get("target_player"); + String reason = (String) tx.get("reason"); + int bal = ((Number) tx.get("balance_after")) + .intValue(); + long time = ((Number) tx.get("time")) + .longValue(); + + boolean pos = amount > 0; + String sign = pos ? "+" : ""; + String color = pos ? "§a" : "§c"; + + StringBuilder sb = new StringBuilder(); + sb.append("§7[") + .append(SDF.format(new Date(time))) + .append("] ") + .append(formatTxType(type)) + .append(" ") + .append(color).append(sign) + .append(amount).append("§7"); + if (tgt != null && !tgt.isEmpty()) + sb.append(" §e↔ ").append(tgt); + if (reason != null && !reason.isEmpty()) + sb.append(" §7(").append(reason) + .append(")"); + sb.append(" §7余额:§f").append(bal); + return sb.toString(); + } + + private String formatTxType(String type) { + if (type == null) return "§7未知"; + switch (type) { + case BondManager.TX_ADMIN_GIVE: + return "§a管理员发放"; + case BondManager.TX_ADMIN_DEDUCT: + return "§c管理员扣除"; + case BondManager.TX_TRANSFER_OUT: + return "§c转出"; + case BondManager.TX_TRANSFER_IN: + return "§a转入"; + case BondManager.TX_REDEEM: + return "§aCDK兑换"; + case BondManager.TX_SHOP_BUY: + return "§e商城购买"; + case BondManager.TX_DAILY_SIGN: + return "§a签到奖励"; + case BondManager.TX_FREEZE: + return "§c冻结"; + case BondManager.TX_UNFREEZE: + return "§a解冻"; + default: + return "§7" + type; + } + } + + private void showHelp(CommandSender sender) { + sender.sendMessage("§e§l债券命令帮助:"); + sender.sendMessage( + "§e/cypay §7- 查询自己余额"); + sender.sendMessage( + "§e/cypay <玩家> §7- 查余额(管理员)"); + sender.sendMessage( + "§e/cypay <玩家> <金额> §7- 转账"); + if (hasAdminPerm(sender)) { + sender.sendMessage( + "§e/cypay give <玩家> <金额> §7- 给债券"); + sender.sendMessage( + "§e/cypay remove <玩家> <金额> §7- 扣债券"); + sender.sendMessage( + "§e/cypay import <文件> §7- 导入CDK"); + sender.sendMessage( + "§e/cypay freeze <玩家> §7- 冻结"); + sender.sendMessage( + "§e/cypay unfreeze <玩家> §7- 解冻"); + sender.sendMessage( + "§e/cypay water <玩家> [天数] §7- 流水"); + sender.sendMessage( + "§e/cypay printer §7- 导出全服流水xlsx"); + sender.sendMessage( + "§e/cypay printer <玩家> §7- 导出指定玩家xlsx"); + + } + } + + @Override + public List onTabComplete( + CommandSender sender, Command command, + String label, String[] args) { + List list = new ArrayList<>(); + + if (args.length == 1) { + String last = args[0].toLowerCase(); + if (hasAdminPerm(sender)) { + list.add("give"); + list.add("remove"); + list.add("import"); + list.add("freeze"); + list.add("unfreeze"); + list.add("water"); + list.add("printer"); + + File cdkDir = new File( + plugin.getDataFolder(), + "cdk"); + if (cdkDir.exists()) { + File[] files = cdkDir.listFiles( + (d, n) -> n + .endsWith(".txt")); + if (files != null) + for (File f : files) + list.add(f.getName()); + } + } + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, last); + } + + if (args.length == 2) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first) + || "freeze".equals(first) + || "unfreeze".equals(first) + || "water".equals(first)) { + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, + args[1].toLowerCase()); + } + list.add("10"); + list.add("50"); + list.add("100"); + return filter(list, + args[1].toLowerCase()); + } + + if (args.length == 3) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first)) { + list.add("10"); + list.add("50"); + list.add("100"); + list.add("999"); + return filter(list, + args[2].toLowerCase()); + } + if ("water".equals(first)) { + list.add("1"); + list.add("3"); + list.add("7"); + list.add("14"); + return filter(list, + args[2].toLowerCase()); + } + } + + return list; + } +} diff --git a/backup/src/main/java/Sdf1_login/DatabaseManager.java b/backup/src/main/java/Sdf1_login/DatabaseManager.java new file mode 100644 index 0000000..cb2b8f7 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/DatabaseManager.java @@ -0,0 +1,1758 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.logging.Logger; + +public class DatabaseManager { + + private Connection db; + private final File dataFolder; + private final Logger logger; + + public DatabaseManager(File dataFolder) { + this.dataFolder = dataFolder; + this.logger = Logger.getLogger("Sdf1_login"); + } + + // ==================== 初始化 ==================== + + public void init() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(dataFolder, + "login.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 用户表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "users (" + + "player_name TEXT PRIMARY KEY," + + "password_hash TEXT NOT NULL," + + "password_salt TEXT NOT NULL," + + "email TEXT DEFAULT ''," + + "register_time INTEGER DEFAULT 0," + + "last_login_time INTEGER DEFAULT 0," + + "total_online_time INTEGER DEFAULT 0," + + "last_online_check INTEGER DEFAULT 0," + + "is_logged_in INTEGER DEFAULT 0," + + "blocks_broken INTEGER DEFAULT 0," + + "blocks_placed INTEGER DEFAULT 0," + + "gift_stage INTEGER DEFAULT 0," + + "gift_claimed TEXT DEFAULT ''," + + "invite_code TEXT DEFAULT ''," + + "invited_by TEXT DEFAULT ''," + + "monthly_invite_count INTEGER DEFAULT 0," + + "invite_month TEXT DEFAULT ''," + + "points INTEGER DEFAULT 0," + + "checkin_streak INTEGER DEFAULT 0," + + "last_checkin_date TEXT DEFAULT ''," + + "total_checkin_days INTEGER DEFAULT 0," + + "temp_password TEXT DEFAULT ''," + + "temp_pw_expire INTEGER DEFAULT 0," + + "temp_pw_used INTEGER DEFAULT 0," + + "ip_address TEXT DEFAULT ''," + + "tasks_completed TEXT DEFAULT ''," + + "premium_uuid TEXT DEFAULT ''," + + "register_ip TEXT DEFAULT ''," + + "register_type TEXT DEFAULT 'manual'," + + "backcheck_dates TEXT DEFAULT ''" + + ")"); + + + // 安全添加 users 缺失列 + safeAdd(st, "tasks_completed", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_password", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_pw_expire", + "INTEGER DEFAULT 0"); + safeAdd(st, "temp_pw_used", + "INTEGER DEFAULT 0"); + safeAdd(st, "ip_address", + "TEXT DEFAULT ''"); + safeAdd(st, "premium_uuid", + "TEXT DEFAULT ''"); + safeAdd(st, "register_ip", + "TEXT DEFAULT ''"); + safeAdd(st, "register_type", + "TEXT DEFAULT 'manual'"); + + // ===== 背包备份表:安全迁移 ===== + // 检查旧表是否存在(用 inventory_data 列判断) + boolean hasOldTable = false; + try { + ResultSet rs = st.executeQuery( + "SELECT inventory_data " + + "FROM inventory_backups " + + "LIMIT 1"); + hasOldTable = true; + rs.close(); + } catch (SQLException e) { + hasOldTable = false; + } + + if (hasOldTable) { + // 旧表存在,迁移数据到新表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups_new (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + // 迁移旧数据 + try { + st.execute("INSERT INTO " + + "inventory_backups_new " + + "(player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience) " + + "SELECT player_name, " + + "inventory_data, " + + "armor_data, " + + "extra_data, " + + "player_level, " + + "player_exp " + + "FROM inventory_backups"); + } catch (SQLException ignored) { + } + st.execute("DROP TABLE " + + "inventory_backups"); + st.execute("ALTER TABLE " + + "inventory_backups_new " + + "RENAME TO " + + "inventory_backups"); + } else { + // 无旧表或已迁移,直接创建 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + } + + // 安全报警表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "security_alerts (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "alert_ip TEXT NOT NULL," + + "alert_time INTEGER NOT NULL," + + "alert_date TEXT NOT NULL," + + "notified INTEGER DEFAULT 0," + + "notified_time INTEGER DEFAULT 0)"); + // PVP区域表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "name TEXT NOT NULL UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER DEFAULT 0" + + ")"); + +// PVP击杀总榜表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT NOT NULL," + + "region_name TEXT NOT NULL," + + "kills INTEGER DEFAULT 0," + + "deaths INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, region_name)" + + ")"); + + +// 菜单图标表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "menu_icons (" + + "player_name TEXT PRIMARY KEY," + + "icon_b64 TEXT DEFAULT ''," + + "item_name TEXT DEFAULT ''," + + "save_time INTEGER DEFAULT 0" + + ")"); + + // 工单表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "tickets (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "type TEXT NOT NULL," + + "status TEXT DEFAULT 'pending'," + + "priority TEXT DEFAULT 'normal'," + + "requester TEXT NOT NULL," + + "assigned_to TEXT DEFAULT ''," + + "title TEXT DEFAULT ''," + + "description TEXT DEFAULT ''," + + "reward_amount INTEGER DEFAULT 0," + + "score INTEGER DEFAULT 0," + + "target_player TEXT DEFAULT ''," + + "create_time INTEGER NOT NULL," + + "update_time INTEGER DEFAULT 0," + + "complete_time INTEGER DEFAULT 0," + + "admin_confirmed INTEGER DEFAULT 0," + + "reject_reason TEXT DEFAULT ''" + + ")"); + + // 工单回复表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "ticket_replies (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "ticket_id INTEGER NOT NULL," + + "sender TEXT NOT NULL," + + "message TEXT NOT NULL," + + "create_time INTEGER NOT NULL" + + ")"); + + // 服务商表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "service_providers (" + + "player_name TEXT PRIMARY KEY," + + "role TEXT DEFAULT 'waiter'," + + "active INTEGER DEFAULT 1," + + "join_time INTEGER DEFAULT 0" + + ")"); + // 垃圾回收站物品表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "items (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "item_data TEXT DEFAULT ''," + + "amount INTEGER DEFAULT 1," + + "create_time INTEGER DEFAULT 0" + + ")"); + safeAdd(st, "menu_snowball", + "INTEGER DEFAULT 1"); + st.close(); + logger.info("[Sdf1_login] 数据库初始化完成"); + } catch (Exception e) { + throw new RuntimeException( + "[Sdf1_login] DB初始化失败: " + + e.getMessage(), e); + } + } + + + private void safeAdd(Statement st, + String col, String def) { + try { + st.executeUpdate("ALTER TABLE users " + + "ADD COLUMN " + col + " " + def); + } catch (SQLException ignored) { + } + } + + public void close() { + try { + if (db != null && !db.isClosed()) + db.close(); + } catch (Exception ignored) { + } + } +// ==================== 菜单图标 ==================== + + public void saveMenuIcon(String name, + String iconB64, String itemName) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO menu_icons " + + "(player_name, icon_b64, " + + "item_name, save_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, iconB64); + ps.setString(3, itemName); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT icon_b64 FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + String b64 = null; + if (rs.next()) { + b64 = rs.getString("icon_b64"); + } + rs.close(); + ps.close(); + return b64; + } catch (SQLException e) { + return null; + } + } + + public void deleteMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM menu_icons " + + "WHERE player_name=? " + + "AND icon_b64 IS NOT NULL " + + "AND icon_b64 != '' " + + "LIMIT 1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + // ==================== 用户操作 ==================== + + public boolean userExists(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public void createUser(String name, String hash, + String salt) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO users " + + "(player_name, " + + "password_hash, " + + "password_salt, " + + "register_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, hash); + ps.setString(3, salt); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ========== 临时密码管理 ========== + + public void clearTempPassword(String name) { + setField(name, "temp_password", null); + setField(name, "temp_pw_expire", 0); + setField(name, "temp_pw_used", 1); + } + + public void clearAllTempPasswords() { + try { + Statement st = db.createStatement(); + st.executeUpdate( + "UPDATE users SET " + + "temp_password=NULL," + + "temp_pw_expire=0," + + "temp_pw_used=1 " + + "WHERE temp_password " + + "IS NOT NULL"); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPlayerIPByName( + String name) { + Object ip = getField(name, "ip"); + return ip != null + ? String.valueOf(ip) : null; + } + + public boolean checkPasswordOrTemp( + String name, String hash) { + // 先检查主密码 + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String mainHash = + rs.getString("password_hash"); + rs.close(); + ps.close(); + if (hash.equals(mainHash)) + return true; + } else { + rs.close(); + ps.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + // 再检查临时密码 + try { + PreparedStatement ps2 = db.prepareStatement( + "SELECT temp_password, " + + "temp_pw_expire, " + + "temp_pw_used " + + "FROM users " + + "WHERE player_name=?"); + ps2.setString(1, name); + ResultSet rs2 = ps2.executeQuery(); + if (rs2.next()) { + String tempHash = + rs2.getString( + "temp_password"); + long expire = + rs2.getLong( + "temp_pw_expire"); + int used = + rs2.getInt( + "temp_pw_used"); + rs2.close(); + ps2.close(); + if (tempHash != null + && !tempHash.isEmpty() + && hash.equals(tempHash) + && System.currentTimeMillis() + < expire + && used != 1) { + return true; + } + } else { + rs2.close(); + ps2.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + public Map getUser(String name) { + Map r = + new LinkedHashMap<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + r.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return r; + } + + public void setField(String name, String field, + Object val) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE users SET " + field + + "=? " + + "WHERE player_name=?"); + if (val instanceof Integer) + ps.setInt(1, (Integer) val); + else if (val instanceof Long) + ps.setLong(1, (Long) val); + else + ps.setString(1, + String.valueOf(val)); + ps.setString(2, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Object getField(String name, + String field) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT " + field + + " FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + Object val = rs.next() + ? rs.getObject(1) : null; + rs.close(); + ps.close(); + return val; + } catch (SQLException e) { + return null; + } + } + + public boolean checkPassword(String name, + String hash) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String stored = + rs.getString("password_hash"); + rs.close(); + ps.close(); + return hash.equals(stored); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + + public boolean isUsingTempPassword(String name) { + String h = (String) getField(name, + "temp_password"); + return h != null && !h.isEmpty(); + } + + public void setLoggedIn(String name, + boolean val) { + setField(name, "is_logged_in", + val ? 1 : 0); + } + + public void addPoints(String name, int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + setField(name, "points", curVal + amount); + } + + public boolean deductPoints(String name, + int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + if (curVal < amount) return false; + setField(name, "points", + curVal - amount); + return true; + } + public List> + getAllUsers() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM users"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + public List> + getTicketsByProviderAll( + String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deleteUser(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void recordIP(String name, String ip) { + if (ip != null && !ip.isEmpty()) { + setField(name, "ip_address", ip); + } + } + + // ==================== IP查询 ==================== + + /** + * 统计指定IP注册的账号数量 + */ + public int countIPAccounts(String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address=?"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count; + } catch (SQLException e) { + return 0; + } + } + + /** + * 获取指定IP下所有账号详情 + */ + public List> + getAccountDetailsByIP(String ip) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, " + + "register_time " + + "FROM users " + + "WHERE ip_address=? " + + "ORDER BY register_time"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("register_time", + rs.getLong("register_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 背包备份 ==================== + + public void saveInventoryBackup(String name, + String contents, String armor, + String extra, int level, + double experience) { + try { + PreparedStatement del = + db.prepareStatement( + "DELETE FROM " + + "inventory_backups " + + "WHERE player_name=?"); + del.setString(1, name); + del.executeUpdate(); + del.close(); + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO " + + "inventory_backups" + + " (player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience, " + + "save_time) " + + "VALUES " + + "(?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, contents); + ps.setString(3, armor); + ps.setString(4, extra); + ps.setInt(5, level); + ps.setDouble(6, experience); + ps.setLong(7, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public String[] loadInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time " + + "DESC LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void deleteInventoryBackup( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM inventory_backups " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + boolean has = rs.next(); + rs.close(); + ps.close(); + return has; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +// ==================== PVP区域 ==================== + + public void savePVPRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions " + + "(name, world, " + + "x1, y1, z1, " + + "x2, y2, z2, " + + "create_time) " + + "VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map + getPVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getAllPVPRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + db.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deletePVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +// ==================== PVP击杀 ==================== + + public int getPVPKills( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int kills = 0; + if (rs.next()) + kills = rs.getInt("kills"); + rs.close(); + ps.close(); + return kills; + } catch (SQLException e) { + return 0; + } + } + + public int getPVPDeaths( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int deaths = 0; + if (rs.next()) + deaths = rs.getInt("deaths"); + rs.close(); + ps.close(); + return deaths; + } catch (SQLException e) { + return 0; + } + } + + public void addPVPKill( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,1,0) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "kills=kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void addPVPDeath( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,0,1) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "deaths=deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getPVPKillTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT player_name, " + + "kills, deaths " + + "FROM pvp_kills " + + "WHERE " + + "region_name=? " + + "AND kills>0 " + + "ORDER BY kills " + + "DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + // ==================== 安全报警 ==================== + + public void recordSecurityAlert( + String playerName, String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO security_alerts " + + "(player_name, alert_ip, " + + "alert_time, alert_date) " + + "VALUES (?,?,?,?)"); + ps.setString(1, playerName); + ps.setString(2, ip); + long now = System.currentTimeMillis(); + ps.setLong(3, now); + ps.setString(4, + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date( + now))); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + public List> + getUnnotifiedAlerts( + String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM security_alerts " + + "WHERE player_name=? " + + "AND notified=0 " + + "ORDER BY alert_time ASC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void markAlertNotified(int alertId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE security_alerts " + + "SET notified=1, " + + "notified_time=? " + + "WHERE id=?"); + ps.setLong(1, + System.currentTimeMillis()); + ps.setInt(2, alertId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + /** + * 获取玩家最近N条背包备份 + */ + public List> + getInventoryBackups(String name, + int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id, save_time " + + "FROM inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time DESC " + + "LIMIT ?"); + ps.setString(1, name); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", + rs.getInt("id")); + row.put("save_time", + rs.getLong("save_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + /** + * 获取指定ID的备份数据 + */ + public String[] getInventoryBackupById( + int backupId) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM inventory_backups " + + "WHERE id=?"); + ps.setInt(1, backupId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + + // ==================== 工单操作 ==================== + + public int createTicket(String type, + String status, String priority, + String requester, String title, + String description, + int rewardAmount, + String targetPlayer) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO tickets " + + "(type, status, priority, " + + "requester, title, " + + "description, " + + "reward_amount, " + + "target_player, " + + "create_time) " + + "VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, type); + ps.setString(2, status); + ps.setString(3, priority); + ps.setString(4, requester); + ps.setString(5, title); + ps.setString(6, description); + ps.setInt(7, rewardAmount); + ps.setString(8, targetPlayer); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ResultSet rs = + ps.getGeneratedKeys(); + int id = -1; + if (rs.next()) id = rs.getInt(1); + rs.close(); + ps.close(); + return id; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + public void updateTicketStatus(int ticketId, + String status) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET status=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, status); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void assignTicket(int ticketId, + String provider) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "assigned_to=?, " + + "status='processing', " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, provider); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void completeTicket(int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='completed', " + + "complete_time=?, " + + "update_time=? " + + "WHERE id=?"); + long now = System.currentTimeMillis(); + ps.setLong(1, now); + ps.setLong(2, now); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void confirmTicket(int ticketId, + int score) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "admin_confirmed=1, " + + "score=?, " + + "status='resolved', " + + "update_time=? " + + "WHERE id=?"); + ps.setInt(1, score); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public void rejectTicketComplete(int ticketId, + String reason) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='rejected', " + + "reject_reason=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, reason); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 更新工单任意字段 + */ + public void updateTicketField(int ticketId, + String field, Object value) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + field + + "=? WHERE id=?"); + if (value instanceof Integer) + ps.setInt(1, (Integer) value); + else if (value instanceof Long) + ps.setLong(1, (Long) value); + else + ps.setString(1, + String.valueOf(value)); + ps.setInt(2, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map getTicket( + int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE id=?"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getTicketsByStatus(String status) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status=? " + + "ORDER BY create_time DESC"); + ps.setString(1, status); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void addTicketReply(int ticketId, + String sender, String message) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO ticket_replies " + + "(ticket_id, sender, " + + "message, create_time) " + + "VALUES (?,?,?,?)"); + ps.setInt(1, ticketId); + ps.setString(2, sender); + ps.setString(3, message); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + // 更新工单状态为已回复 + updateTicketStatus(ticketId, + "replied"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getTicketReplies(int ticketId) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM ticket_replies " + + "WHERE ticket_id=? " + + "ORDER BY create_time ASC"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + /** + * 获取已回复但超时未互动的工单 + */ + public List> + getRepliedStaleTickets( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='replied' " + + "AND update_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByPlayer(String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE requester=? " + + "ORDER BY create_time DESC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByProvider(String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "AND (status='processing' " + + "OR status='replied') " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getUnconfirmedCompleted( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='completed' " + + "AND admin_confirmed=0 " + + "AND complete_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 服务商操作 ==================== + + public void addServiceProvider(String name, + String role) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO " + + "service_providers " + + "(player_name, role, " + + "active, join_time) " + + "VALUES (?,?,1,?)"); + ps.setString(1, name); + ps.setString(2, role); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void removeServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM service_providers " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean isServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM service_providers " + + "WHERE player_name=? " + + "AND active=1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public List> + getAllServiceProviders() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM service_providers " + + "WHERE active=1"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Connection getDb() { + return db; + } +} diff --git a/backup/src/main/java/Sdf1_login/EmailManager.java b/backup/src/main/java/Sdf1_login/EmailManager.java new file mode 100644 index 0000000..b6906e5 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/EmailManager.java @@ -0,0 +1,284 @@ +package Sdf1_login; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Base64; +import java.util.logging.Logger; + +public class EmailManager { + + private final ConfigManager config; + private final Logger logger; + + public EmailManager(ConfigManager config) { + this.config = config; + this.logger = Logger.getLogger("Sdf1_login"); + } + + public boolean isConfigured() { + String host = config.getSmtp("smtp地址"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + return host != null && !host.isEmpty() + && user != null && !user.isEmpty() + && pass != null && !pass.isEmpty(); + } + + public boolean sendVerifyCode(String to, + String code) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置," + + "无法发送验证码"); + return false; + } + logger.info("[Sdf1_login] 发送验证码到 " + to); + return sendEmailSync(to, + "[Sdf1_login] 验证码", + "您的验证码: " + code + + "\n5分钟内有效。\n" + + "如非本人操作请忽略。"); + } + + public boolean sendTempPassword(String to, + String player, String tempPwd) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, + "[Sdf1_login] 临时密码", + "玩家 " + player + + " 的临时密码: " + tempPwd + + "\n请尽快登录并修改密码!"); + } + + public boolean sendBody(String to, String subject, String body) { + if (!isConfigured()) { + logger.warning("[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, subject, body); + } + + + private boolean sendEmailSync(String to, + String subject, String body) { + final boolean[] result = {false}; + Thread t = new Thread(() -> { + try { + result[0] = sendEmail( + to, subject, body); + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件线程异常: " + + e.getMessage()); + } + }, "Sdf1_login-SMTP"); + t.setDaemon(true); + t.start(); + try { + t.join(15000); + } catch (InterruptedException ignored) { + } + if (t.isAlive()) { + t.interrupt(); + logger.severe( + "[Sdf1_login] SMTP超时(15秒)"); + return false; + } + return result[0]; + } + + private boolean sendEmail(String to, + String subject, String body) { + String host = config.getSmtp("smtp地址"); + String portStr = config.getSmtp("smtp端口"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + String fromName = config.getSmtp( + "发件人名称"); + + int port; + try { + port = Integer.parseInt(portStr); + } catch (Exception e) { + port = 465; + } + + try { + String encSubject = "=?UTF-8?B?" + + Base64.getEncoder() + .encodeToString( + subject.getBytes("UTF-8")) + + "?="; + String encBody = Base64.getEncoder() + .encodeToString( + body.getBytes("UTF-8")); + + if (port == 465) { + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket socket = (SSLSocket) + factory.createSocket( + host, port); + socket.setSoTimeout(10000); + doSmtp(socket, host, user, pass, + to, encSubject, encBody, + fromName); + socket.close(); + } else { + Socket plainSocket = + new Socket(host, port); + plainSocket.setSoTimeout(10000); + BufferedReader preReader = + new BufferedReader( + new InputStreamReader( + plainSocket + .getInputStream(), + "UTF-8")); + PrintWriter preWriter = + new PrintWriter( + plainSocket + .getOutputStream(), + true); + checkResponse(preReader, "220"); + sendLine(preWriter, + "EHLO " + host); + checkResponse(preReader, "250"); + sendLine(preWriter, "STARTTLS"); + checkResponse(preReader, "220"); + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket sslSocket = (SSLSocket) + factory.createSocket( + plainSocket, host, + port, true); + sslSocket.startHandshake(); + doSmtp(sslSocket, host, user, pass, + to, encSubject, encBody, + fromName); + sslSocket.close(); + } + logger.info( + "[Sdf1_login] 邮件发送成功 -> " + + to); + return true; + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件异常: " + + e.getClass() + .getSimpleName() + + ": " + e.getMessage()); + if (e.getCause() != null) { + logger.severe( + "[Sdf1_login] 根因: " + + e.getCause() + .getMessage()); + } + return false; + } + } + + private void doSmtp(SSLSocket socket, + String host, String user, String pass, + String to, String subject, String body, + String fromName) throws Exception { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + socket.getInputStream(), + "UTF-8")); + OutputStream rawOut = + socket.getOutputStream(); + PrintWriter writer = + new PrintWriter( + new java.io.BufferedOutputStream( + rawOut), true); + + checkResponse(reader, "220"); + sendLine(writer, "EHLO " + host); + checkResponse(reader, "250"); + sendLine(writer, "AUTH LOGIN"); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + user.getBytes("UTF-8"))); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + pass.getBytes("UTF-8"))); + checkResponse(reader, "235"); + sendLine(writer, + "MAIL FROM:<" + user + ">"); + checkResponse(reader, "250"); + sendLine(writer, + "RCPT TO:<" + to + ">"); + checkResponse(reader, "250"); + sendLine(writer, "DATA"); + checkResponse(reader, "354"); + sendLine(writer, + "From: " + fromName + + " <" + user + ">"); + sendLine(writer, + "To: <" + to + ">"); + sendLine(writer, + "Reply-To: <" + user + ">"); + sendLine(writer, + "Subject: " + subject); + sendLine(writer, "MIME-Version: 1.0"); + sendLine(writer, + "Content-Type: text/plain; " + + "charset=UTF-8"); + sendLine(writer, + "Content-Transfer-Encoding: " + + "base64"); + sendLine(writer, ""); + sendLine(writer, body); + sendLine(writer, "."); + checkResponse(reader, "250"); + sendLine(writer, "QUIT"); + } + + private void sendLine(PrintWriter writer, + String line) { + writer.print(line + "\r\n"); + writer.flush(); + } + + private void checkResponse( + BufferedReader reader, + String expected) throws Exception { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + if (line.length() >= 4 + && Character.isDigit( + line.charAt(0)) + && line.charAt(3) == ' ') + break; + } + String resp = sb.toString().trim(); + if (!resp.startsWith(expected)) { + if (resp.startsWith("535")) + logger.severe( + "[Sdf1_login] SMTP 535 " + + "认证失败"); + throw new Exception( + "SMTP响应异常: 期望 " + + expected + + ",实际: " + resp); + } + } +} diff --git a/backup/src/main/java/Sdf1_login/GUIManager.java b/backup/src/main/java/Sdf1_login/GUIManager.java new file mode 100644 index 0000000..cbc7102 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/GUIManager.java @@ -0,0 +1,2681 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + + + + +import java.util.*; + +public class GUIManager implements Listener { + + private final Main plugin; + + public static final String T_MAIN = + "§6§l服务器菜单"; + public static final String T_MORE = + "§6§l更多功能"; + public static final String T_EDITOR = + "§6§l编辑菜单项"; + public static final String T_BIOME = + "§b§l目标群系传送"; + public static final String T_MY_INFO = + "§6§l我的信息"; + public static final String T_GIFT_STAGES = + "§6§l礼包阶段"; + public static final String T_ADMIN = + "§6§l管理员面板"; + public static final String T_INVITE = + "§6§l邀请数据"; + public static final String T_USER_MGMT = + "§6§l用户管理"; + public static final String T_TASK_CENTER = + "§6§l任务中心"; + private final Map userMgmtPages = new HashMap<>(); + private static final int UMGMT_PAGE_SIZE = 45; + + + + public GUIManager(Main plugin) { + this.plugin = plugin; + } + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_MAIN); +// ===== 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + + // 已占用的槽位 + Set occupied = new HashSet<>(); + occupied.add(10); + occupied.add(12); + occupied.add(14); + occupied.add(16); + occupied.add(19); + occupied.add(21); + occupied.add(23); + occupied.add(25); + occupied.add(29); + occupied.add(31); + occupied.add(32); + occupied.add(34); + + // 自定义区域:第1排(0-8) + 第5排(36-44) + 第6排(45-53) + int[] customArea = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53 + }; + + int autoIdx = 0; + for (int ci = 0; + ci < customs.size() && ci < 27; ci++) { + MenuManager.MenuItem mi = + customs.get(ci); + + // 权限过滤 + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + + int targetSlot = -1; + + // 玩家指定了格子? + if (mi.slot != null) { + targetSlot = findBestSlot( + mi.slot - 1, occupied); + } + + // 未指定或冲突 → 自动排队 + if (targetSlot < 0) { + while (autoIdx < customArea.length) { + int s = customArea[autoIdx++]; + if (!occupied.contains(s)) { + targetSlot = s; + break; + } + } + } + + if (targetSlot < 0 + || targetSlot >= 54) continue; + occupied.add(targetSlot); + + // 放置物品 + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(java.util.Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(targetSlot, icon); + } + } + +// 背景填充 + fillBg(g); + p.openInventory(g); + + // ===== Row1(9-17): 核心功能 ===== + g.setItem(10, mkItem(Material.PLAYER_HEAD, + "§a§l我的信息")); + g.setItem(12, mkItem(Material.PAPER, + "§b§l邀请数据")); + g.setItem(14, mkItem(Material.EMERALD, + "§d§l积分商城")); + g.setItem(16, mkItem(Material.COMPASS, + "§b§l群系传送", + "§7传送到指定群系")); + + // ===== Row2(18-26): 日常功能 ===== + g.setItem(19, mkItem(Material.BOOK, + "§6工单系统")); + g.setItem(21, mkItem(Material.EMERALD_BLOCK, + "§a§l每日签到", + "§7签到获取积分和惊喜奖励")); + g.setItem(23, mkItem(Material.ENDER_CHEST, + "§6§l任务中心")); + if (p.isOp() || isAdmin(p)) { + g.setItem(25, mkItem(Material.HOPPER, + "§6§l垃圾回收站", + "§7查看和取回清理的物品")); + + + } + // ===== Row3(27-35): 管理功能 ===== + // ★ 商店 - 所有人都能看 ★ + g.setItem(27, mkItem(Material.CHEST, + "§6§l商店", + "§7浏览、购买、出售物品", + "§7货币: 债券")); + + if (isAdmin(p)) { + g.setItem(29, mkItem( + Material.REDSTONE_BLOCK, + "§c§l管理员面板")); + } + if (p.isOp()) { + g.setItem(31, mkItem(Material.ECHO_SHARD, + "§c§l管理菜单", + "§7添加、编辑、删除菜单项")); + } + // 在 openMain 方法中,Row3 区域加: + + // ★ 余额查询 - 所有人都能看 ★ + double myBal = 0; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault.economy + .Economy.class); + if (reg != null) + myBal = reg.getProvider() + .getBalance(p); + } catch (Exception ignored) {} + int myBonds = plugin.getBonds().getBonds(p.getName()); + g.setItem(32, mkItem(Material.EMERALD, + "§b§l我的钱包", + "§7查看债券余额与流水记录")); + +// ★ 余额操作 - 仅OP和tag玩家 ★ + if (isAdmin(p)) { + g.setItem(34, mkItem(Material.GOLD_INGOT, + "§6§l余额操作", + "§7给钱、扣钱")); + } + + + + // ===== Row0(0-8): 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + for (int i = 0; + i < customs.size() && i < 9; + i++) { + MenuManager.MenuItem mi = + customs.get(i); + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(i, icon); + } + } + + // 背景最后填充(不覆盖物品) + fillBg(g); + p.openInventory(g); + } + private int findBestSlot( + int requested, + Set occupied) { + // 目标位空闲直接用 + if (requested >= 0 && requested < 54 + && !occupied.contains(requested)) { + return requested; + } + // BFS上下左右找最近空位 + int row = requested / 9; + int col = requested % 9; + boolean[][] visited = + new boolean[6][9]; + java.util.Queue queue = + new java.util.LinkedList<>(); + queue.add(new int[]{row, col}); + visited[row][col] = true; + int[][] dirs = { + {-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + while (!queue.isEmpty()) { + int[] cur = queue.poll(); + for (int[] d : dirs) { + int nr = cur[0] + d[0]; + int nc = cur[1] + d[1]; + if (nr >= 0 && nr < 6 + && nc >= 0 && nc < 9 + && !visited[nr][nc]) { + visited[nr][nc] = true; + int s = nr * 9 + nc; + if (!occupied.contains(s)) { + return s; + } + queue.add(new int[]{nr, nc}); + } + } + } + return -1; + } + + public void openMenuManager(Player p) { + List list = + plugin.getMenu().getItems(); + Inventory g = Bukkit.createInventory( + null, 27, + "§c§l菜单管理"); + fillBg(g); + + for (int i = 0; + i < list.size() && i < 9; i++) { + MenuManager.MenuItem mi = list.get(i); + g.setItem(i, mkItem( + mi.icon.getType(), + "§e" + mi.title, + "§7指令: " + mi.command, + "§7权限: " + mi.permType, + "", + "§a左键编辑", + "§7右键切换权限")); + } + + g.setItem(22, mkItem(Material.LIME_WOOL, + "§a§l添加菜单项")); + + g.setItem(26, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + + public void openBalanceOps(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§d§l余额操作"); + fillBg(g); + + net.milkbowl.vault.economy.Economy eco = null; + try { + var reg = plugin.getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); + if (reg != null) eco = reg.getProvider(); + } catch (Exception ignored) {} + + int slot = 0; + for (Player op : Bukkit.getOnlinePlayers()) { + if (slot >= 45) break; + double bal = 0; + if (eco != null) bal = eco.getBalance(op); + boolean isSelf = op.getName().equals(p.getName()); + int bondBal = plugin.getBonds().getBonds(op.getName()); + g.setItem(slot, mkItem( + isSelf ? Material.EMERALD_BLOCK : Material.PLAYER_HEAD, + "§e" + op.getName(), + "§7金币: §a$" + String.format("%.2f", bal), + "§7债券: §e" + bondBal + " §6枚", + "", + "§a左键经济(±) §6右键债券(±)")); + slot++; + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + p.openInventory(g); + } + + public void openShop(Player p) { + p.closeInventory(); + plugin.getShopManager().openShopMain(p); + } + + public void openTicket(Player p) { + p.closeInventory(); + p.performCommand("sdf1_login ticket"); + } + @EventHandler + public void onInvClose( + + InventoryCloseEvent e) { + if (!(e.getPlayer() instanceof Player)) + return; + if (!(e.getPlayer() instanceof Player)) return; + Player p = (Player) e.getPlayer(); + Player p3 = (Player) e.getPlayer(); + String t = e.getView().getTitle(); + if (!(e.getPlayer() instanceof Player)) return; + Player p2 = (Player) e.getPlayer(); + + if (!t.equals(T_EDITOR)) return; + + // ★ 编辑器关闭时清理空项 ★ + List list = + plugin.getMenu().getItems(); + if (!list.isEmpty()) { + MenuManager.MenuItem last = + list.get(list.size() - 1); + if ((last.command == null + || last.command.trim().isEmpty() + || last.command.trim() + .equals("/")) + && "新菜单项" + .equals(last.title)) { + list.remove(list.size() - 1); + } + } + } + + public void openSubMenu( + Player p, String fileName) { + String menuTitle = + "§6§l" + fileName + .replace(".txt", ""); + Inventory g = Bukkit.createInventory( + null, 54, menuTitle); + fillBg(g); + + java.io.File f = new java.io.File( + plugin.getDataFolder(), fileName); + if (!f.exists()) { + p.sendMessage("§c文件不存在: " + + fileName); + return; + } + try { + List lines = + java.nio.file.Files + .readAllLines( + f.toPath(), + java.nio.charset + .StandardCharsets + .UTF_8); + List items = + new ArrayList<>(); + StringBuilder buf = + new StringBuilder(); + boolean inItem = false; + + for (String raw : lines) { + String l = raw.trim(); + if (l.startsWith("#") + || l.startsWith("//")) + continue; + if (l.contains("")) { + inHtml = false; + l = l.substring( + l.indexOf("-->") + 3) + .trim(); + } else { + plugin.getLogger().info( + "[Menu] L" + lineNum + + ": [HTML注释中]"); + continue; + } + } + + // ===== 第3优先:整行注释(最高裁决) ===== + // 以 # 或 // 开头的行,直接跳过 + // 不管后面包含什么 /* "); + pw.println(); + pw.println("{"); + pw.println(" 标题:主城传送"); + pw.println(" 指令:/spawn"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:COMPASS"); + pw.println("}"); + pw.println(); + pw.println("{"); + pw.println(" 标题:随机传送"); + pw.println(" 指令:/rtp"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:ENDER_PEARL"); + pw.println("}"); + } catch (Exception ignored) { + } + } + + // ===== 保存 ===== + public void saveMenu() { + File f = new File( + plugin.getDataFolder(), + "菜单.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 服务器菜单"); + pw.println(); + for (MenuItem mi : items) { + pw.println("{"); + pw.println(" 标题:" + + mi.title); + pw.println(" 指令:" + + mi.command); + pw.println(" 权限类型:" + + mi.permType); + // ★ 保存实际材质名 ★ + pw.println(" 图标:" + + mi.icon.getType().name()); + pw.println("}"); + pw.println(); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Menu] 保存失败: " + + e.getMessage()); + } + } + + // ===== 聊天编辑 ===== + public boolean isEditing(String name) { + return chatWait.contains(name); + } + + public void editTitle( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "title"); + p.closeInventory(); + p.sendMessage("§e请输入菜单标题:"); + } + + public void editCommand( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "command"); + p.closeInventory(); + p.sendMessage("§e请输入指令" + + "(自动补全/):"); + } + + public void onChat(Player p, String input) { + String name = p.getName(); + if (!chatWait.remove(name)) return; + + String field = chatField.remove(name); + Integer idx = chatIdx.get(name); + if (idx == null || idx >= items.size()) return; + + MenuItem mi = items.get(idx); + + if ("title".equals(field)) { + mi.title = input; + p.sendMessage("§a标题: " + input); + } else if ("command".equals(field)) { + if (!input.startsWith("/")) + input = "/" + input; + mi.command = input; + p.sendMessage("§a指令: " + input); + } + + Bukkit.getScheduler().runTask(plugin, () -> + plugin.gui.openEditor(p, idx)); + } +} diff --git a/backup/src/main/java/Sdf1_login/OrderManager.java b/backup/src/main/java/Sdf1_login/OrderManager.java new file mode 100644 index 0000000..9dcb625 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/OrderManager.java @@ -0,0 +1,1345 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.logging.Level; + +public class OrderManager implements Listener { + private final Main plugin; + private final List orders = + Collections.synchronizedList(new ArrayList<>()); + private final Map userViewPage = new HashMap<>(); + private final Map adminViewPage = new HashMap<>(); + private final Map adminViewTarget = new HashMap<>(); + private final Set partialRefundListening = new HashSet<>(); + private final Map partialRefundTarget = new HashMap<>(); + private final Map receiptBuilderSection = new HashMap<>(); + private String tplHeader = ""; + private String tplItemLine = "{品名} x{数量} {原价}→{执行价} 小计{小计}枚"; + private String tplDiscountLine = "优惠: -{优惠额}枚 ({优惠类型})"; + private String tplPackLine = "打包: +{打包费}枚 ({打包颜色})"; + private String tplSummary = "原价{原价合计} → 实付{实付}"; + private String tplFooter = "感谢惠顾,欢迎再来!"; + private static final long RETENTION_30D = 30L * 24 * 60 * 60 * 1000; + private static final long SELF_REFUND_WINDOW = 5 * 60 * 1000; + private static final int PAGE_SIZE = 21; + private static final int[] SLOTS = { + 10,11,12,13,14,15,16,19,20,21,22,23,24,25,28,29,30,31,32,33,34}; + // ===== 字段 ===== + private final Set shulkerUsedOrders = + Collections.synchronizedSet(new HashSet<>()); + // ===== 待审批的退货退款 ===== + private final Map pendingReturns = + new HashMap<>(); + // ===== 退货申请确认 ===== + private final Map returnConfirmWaiting = + new HashMap<>(); // uuid -> orderId + private static final int RETURN_CONFIRM_TIMEOUT = 600; // 30秒 + + + private static class PendingReturn { + long orderId; + UUID playerUUID; + String playerName; + int totalPaid; + long createTime; + String packColor; + String packType; + List items; + + PendingReturn(OrderRecord r) { + this.orderId = r.orderId; + this.playerUUID = r.uuid; + this.playerName = r.player; + this.totalPaid = r.totalPaid; + this.createTime = System.currentTimeMillis(); + this.packColor = r.packColor; + this.packType = r.packType; + this.items = new ArrayList<>(r.items); + } + } + + + public static class OrderItem { + public String name; public String mat; + public int originalPrice; public int finalPrice; public int qty; + public OrderItem(String n,String m,int op,int fp,int q){ + name=n;mat=m;originalPrice=op;finalPrice=fp;qty=q;} + public int subtotal(){return finalPrice*qty;} + public int originalSubtotal(){return originalPrice*qty;} + } + public static class OrderRecord { + public long orderId; public UUID uuid; public String player; + public List items; public int totalOriginal; public int totalPaid; + public int discount; public String discountType; public String couponCode; + public int packFee; public String packType; public String packColor; + public long timestamp; public long printTime; public int status; + public int refundedAmount; public String refundType; + } + + public OrderManager(Main plugin) { + this.plugin = plugin; + loadReceiptConfig(); + loadOrders(); + startCleanupTask(); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + public List getOrders() { return orders; } + + // ===== 小票模板 ===== + private void loadReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + if (!f.exists()) createDefaultReceiptConfig(f); + Map m = loadMap(f); + tplHeader = m.getOrDefault("头部", tplHeader); + tplItemLine = m.getOrDefault("商品行", tplItemLine); + tplDiscountLine = m.getOrDefault("优惠行", tplDiscountLine); + tplPackLine = m.getOrDefault("打包行", tplPackLine); + tplSummary = m.getOrDefault("汇总", tplSummary); + tplFooter = m.getOrDefault("页脚", tplFooter); + } + private void saveReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=" + tplHeader); + L.add("商品行=" + tplItemLine); + L.add("优惠行=" + tplDiscountLine); + L.add("打包行=" + tplPackLine); + L.add("汇总=" + tplSummary); + L.add("页脚=" + tplFooter); + writeLines(f, L); + } + private void createDefaultReceiptConfig(File f) { + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}"); + L.add("商品行=§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚"); + L.add("优惠行=优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})"); + L.add("打包行=打包: §c+{打包费}枚§r ({打包颜色})"); + L.add("汇总=原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚"); + L.add("页脚=感谢惠顾,欢迎再来!"); + writeLines(f, L); + } + + // ===== 订单记录 ===== + public void recordOrder(Player p, List items, + int totalOriginal, int totalPaid, int discount, + String discountType, String couponCode, int packFee, + String packType, String packColor) { + OrderRecord r = new OrderRecord(); + r.orderId = System.currentTimeMillis(); + r.uuid = p.getUniqueId(); r.player = p.getName(); + r.items = items; r.totalOriginal = totalOriginal; + r.totalPaid = totalPaid; r.discount = discount; + r.discountType = discountType; r.couponCode = couponCode; + r.packFee = packFee; r.packType = packType; + r.packColor = packColor; r.timestamp = r.orderId; + r.printTime = 0; r.status = 0; r.refundedAmount = 0; + r.refundType = null; + orders.add(r); saveOrders(); + } + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + // ===== 小票聊天栏 ===== + /** + * 聊天栏打印小票(给定参数 Player + OrderRecord) + * 不要改动方法签名 + */ + public void sendReceiptChat(Player p, OrderRecord r) { + if (p == null || r == null) return; + + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + p.sendMessage("§7§m "); + p.sendMessage("§6§l======== 商店购物小票 ========"); + + // 退款标记 + String statusTag = getStatusTag(r); + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§7订单号: §f#" + r.orderId); + p.sendMessage("§7下单时间: §f" + + df.format(new Date(r.timestamp))); + p.sendMessage("§7玩家: §f" + r.player); + p.sendMessage("§7------------------------------"); + + for (OrderItem it : r.items) { + String price; + if (it.originalPrice != it.finalPrice) { + price = "§f" + it.originalPrice + + "§7→§6" + it.finalPrice; + } else { + price = "§6" + it.finalPrice; + } + p.sendMessage("§7 §e" + it.name + + " §7x" + it.qty + + " " + price + "枚" + + " §6小计" + it.subtotal() + "枚"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7原价: §f" + r.totalOriginal + "枚"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "§7(优惠券 -" + pct + "%)" + : "eco".equals(r.discountType) + ? "§7(环保 -" + pct + "%)" + : "§7(-" + pct + "%)"; + p.sendMessage("§7优惠: §a-" + r.discount + + "枚 " + dt); + } + + if (r.packFee > 0) { + String cn = r.packColor != null + ? r.packColor + .replace("_SHULKER_BOX", "") + .replace("SHULKER_BOX", "原色") + : "默认"; + p.sendMessage("§7打包: §c+" + r.packFee + + "枚 §7(" + cn + "潜影盒)"); + } else { + p.sendMessage("§7包装: 直接放入背包"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7实付: §6" + r.totalPaid + "枚"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + p.sendMessage("§7余额: §a" + bal + "枚"); + + // 退款标记 + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§6§l=============================="); + r.printTime = System.currentTimeMillis(); + } + + /** + * 生成书本小票内容 + */ + private String buildReceiptText(OrderRecord r) { + String statusTag = getStatusTag(r); + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + StringBuilder sb = new StringBuilder(); + + sb.append("§6§l===== 商店小票 =====\n"); + + // 退款标记 + String tag = getStatusTag(r); + if (!tag.isEmpty()) sb.append(tag).append("\n"); + + sb.append("订单号: #").append(r.orderId).append("\n"); + sb.append("时间: ").append( + df.format(new Date(r.timestamp))).append("\n"); + sb.append("玩家: ").append(r.player).append("\n"); + sb.append("------------------------------\n"); + + for (OrderItem it : r.items) { + String pr = (it.originalPrice != it.finalPrice) + ? it.originalPrice + "->" + it.finalPrice + : String.valueOf(it.finalPrice); + sb.append(it.name).append(" x").append(it.qty) + .append(" ").append(pr).append("枚") + .append(" 小计").append(it.subtotal()) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("原价: ").append(r.totalOriginal) + .append("枚\n"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "优惠券(-" + pct + "%)" + : "环保(-" + pct + "%)"; + sb.append("优惠: -").append(r.discount) + .append("枚 ").append(dt).append("\n"); + } + + if (r.packFee > 0) { + sb.append("打包: +").append(r.packFee) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("实付: ").append(r.totalPaid).append("枚\n"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + sb.append("余额: ").append(bal).append("枚\n"); + + // 退款标记 + if (!statusTag.isEmpty()) sb.append("\n").append(tag).append("\n"); + + return sb.toString(); + } + + /** + * 退款状态标记文本 + */ + private String getStatusTag(OrderRecord r) { + switch (r.status) { + case 2: + return "§c§l<<<<<<< 已全额退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 3: + return "§c§l<<<<<<< 已部分退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 4: + return "§c§l<<<<<<< 退款申请已拒绝 >>>>>>>"; + default: + return ""; + } + } + + /** + * 创建小票书 + */ + public ItemStack createReceiptBook(OrderRecord r) { + String text = buildReceiptText(r); + String[] lines = text.split("\n"); + + ItemStack book = new ItemStack(Material.WRITTEN_BOOK); + BookMeta bm = (BookMeta) book.getItemMeta(); + bm.setTitle("购物小票 #" + r.orderId); + bm.setAuthor("商店"); + + StringBuilder page = new StringBuilder(); + int lineCount = 0; + for (String line : lines) { + // ★ 保留颜色代码 + page.append(line).append("\n"); + lineCount++; + if (lineCount >= 13) { + bm.addPage(page.toString().trim()); + page = new StringBuilder(); + lineCount = 0; + } + } + if (page.length() > 0) { + bm.addPage(page.toString().trim()); + } + + book.setItemMeta(bm); + return book; + } + + // ===== 退款工具 ===== + private boolean withinSelfRefundWindow(OrderRecord r) { return System.currentTimeMillis() - r.timestamp < SELF_REFUND_WINDOW; } + private int countDirectItems(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private int countShulkers(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private void removeItems(Player p, String matName, int amt) { + Material m = Material.matchMaterial(matName); if (m != null) p.getInventory().removeItem(new ItemStack(m, amt)); + } + private boolean removeOneShulker(Player p, + String materialName) { + // 先尝试精确匹配 + Material mat = Material.matchMaterial(materialName); + if (mat != null) { + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + return true; + } + } + } + // 匹配不到,移除任意一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .endsWith("_SHULKER_BOX")) { + p.getInventory().clear(i); + return true; + } + } + return false; + } + public String validateRefund(OrderRecord r, Player p) { + if (r.status != 0) + return "订单状态: " + getStatusName(r.status); + + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // ★ 检查:潜影盒是否被放置过 + if (shulkerUsedOrders.contains(r.orderId)) { + return "§c潜影盒已被使用过,无法自助退款"; + } + + // ★ 检查:背包里是否有同一订单的潜影盒 + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im == null || im.getLore() == null) + continue; + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + return null; // 找到了,可退 + } + } + } + return "§c对应的潜影盒已不在背包中"; + } else { + // ★ 散装:检测物品数量 + for (OrderItem it : r.items) { + int have = countDirectItems(p, it.mat); + if (have < it.qty) + return "§c" + it.name + "不足(有" + + have + "/" + it.qty + ")"; + } + } + return null; + } + + + + private void notifyPlayer(UUID uuid, String msg) { Player p = Bukkit.getPlayer(uuid); if (p != null && p.isOnline()) p.sendMessage(msg); } + private void notifyAdmins(Player p, OrderRecord r) { + String m = "§e[订单] " + p.getName() + " 申请退款 #" + r.orderId + " §6" + r.totalPaid + "枚"; + for (Player a : Bukkit.getOnlinePlayers()) if (isAdmin(a) && !a.equals(p)) a.sendMessage(m); + } + private String getStatusName(int s) { switch(s){case 0:return "正常";case 1:return "申请中";case 2:return "已退";case 3:return "部分退";case 4:return "已拒绝";default:return "未知";} } + private boolean isAdmin(Player p) { + if (p.isOp()) return true; + try { ConfigManager cm = plugin.getConfigMgr(); if (cm == null) return false; return p.hasPermission("sdf1.admin") || p.hasPermission("sdf1." + cm.adminTag); } catch (Exception e) { return p.hasPermission("sdf1.admin"); } + } + + // ===== 订单中心 ===== + public void openOrderCenter(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l订单中心"); + g.setItem(13, mkItem(Material.BOOK, "§e§l购买记录", "§7查看历史订单", "", "§a点击进入")); + if (isAdmin(p)) { g.setItem(31, mkItem(Material.NETHER_STAR, "§c§l管理员面板", "§7在线玩家购物记录", "§7支持退款操作", "", "§a点击进入")); + g.setItem(33, mkItem(Material.CRAFTING_TABLE, "§e§l小票配置", "§7配置小票模板样式", "", "§a点击进入")); } + g.setItem(49, mkItem(Material.ARROW, "§7返回商店")); + p.openInventory(g); + } + + // ===== 购买记录 ===== + public void openMyOrders(Player p, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l购买记录"); + List list = getPlayerAllOrders(p.getUniqueId()); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + userViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon; String st; String act; + if (r.status != 0) { icon = r.status == 1 ? Material.PAPER : Material.BARRIER; st = "§7状态: " + getStatusName(r.status); act = "§b左键: 补打小票"; } + else if (withinSelfRefundWindow(r)) { icon = Material.EMERALD; st = "§7状态: §a可退款(5分钟内)"; act = "§b左键: 补打小票\n§a右键: 退款退货\n§eShift+右键: 仅退款"; } + else { icon = Material.BOOK; st = "§7状态: §7已过自助退款期"; act = "§b左键: 补打小票\n§e右键: 申请退款退货\n§6Shift+右键: 申请仅退款"; } + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7原价: §f" + r.totalOriginal + "枚"); + if (r.discount > 0) lore.add("§7优惠: §a-" + r.discount + "枚"); + lore.add("§7实付: §6" + r.totalPaid + "枚"); lore.add(st); lore.add(""); + for (String line : act.split("\n")) lore.add(line); + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7暂无订单记录")); + addPageButtons(g, page, total); + p.openInventory(g); + } + + // ===== 管理员面板 ===== + public void openAdminPanel(Player p, int page) { + if (!isAdmin(p)) { p.sendMessage("§c权限不足"); return; } + Inventory g = Bukkit.createInventory(null, 54, "§c§l管理员订单面板"); + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int total = Math.max(1, (int) Math.ceil((double) online.length / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); adminViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, online.length); + // ★ 替换 openAdminPanel 中的物品创建部分 + for (int i = start; i < end; i++) { + int si = i - start; + if (si >= SLOTS.length) break; + Player t = online[i]; + List tl = getPlayerOrdersWithin( + t.getUniqueId(), RETENTION_30D); + int ts = 0; + for (OrderRecord r : tl) ts += r.totalPaid; + + ItemStack is = new ItemStack(Material.PLAYER_HEAD); + org.bukkit.inventory.meta.SkullMeta sm = + (org.bukkit.inventory.meta.SkullMeta) + is.getItemMeta(); + if (sm != null) { + sm.setOwner(t.getName()); // ★ 设置头颅皮肤 + sm.setDisplayName("§e" + t.getName()); + sm.setLore(Arrays.asList( + "§7订单: §f" + tl.size() + "笔", + "§7消费: §6" + ts + "枚", + "", "§a点击进入")); + is.setItemMeta(sm); // ★ 这行之前缺失了 + } + g.setItem(SLOTS[si], is); + } + + if (online.length == 0) g.setItem(31, mkItem(Material.BARRIER, "§7无在线玩家")); + addPageButtons(g, page, total); p.openInventory(g); + } + + // ===== 管理员订单 ===== + public void openAdminOrders(Player p, UUID targetUUID, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§c§l订单管理"); + String name = Bukkit.getOfflinePlayer(targetUUID).getName(); + List list = getPlayerOrdersWithin(targetUUID, RETENTION_30D); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + adminViewPage.put(p.getUniqueId(), page); adminViewTarget.put(p.getUniqueId(), targetUUID); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon = r.status == 2 || r.status == 4 ? Material.BARRIER : r.status == 1 ? Material.PAPER : Material.BOOK; + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7实付: §6" + r.totalPaid + "枚"); + if (r.status == 1) { lore.add("§7状态: §e申请中(" + nn(r.refundType) + ")"); lore.add(""); lore.add("§a右键: 批准"); lore.add("§c左键: 拒绝"); } + else if (r.status == 2) lore.add("§7已退: §a" + r.refundedAmount + "枚"); + else if (r.status == 3) lore.add("§7部分退: §a" + r.refundedAmount + "枚"); + else if (r.status == 4) lore.add("§7已拒绝"); + else { lore.add(""); lore.add("§a右键: 整单退款"); lore.add("§e左键: 部分退款(输入%)"); } + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7该玩家暂无订单")); + addPageButtons(g, page, total); + g.setItem(49, mkItem(org.bukkit.Material.PAPER, name + " §e" + (page + 1) + "/" + total)); + p.openInventory(g); + } + private void addPageButtons(Inventory g, int page, int total) { + g.setItem(48, page > 0 ? mkItem(Material.ARROW, "§7上一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + if (g.getItem(49) == null) g.setItem(49, mkItem(Material.PAPER, "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 ? mkItem(Material.ARROW, "§7下一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, "§7返回")); + } + + // ===== 小票配置 ===== + public void openReceiptConfig(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l小票配置"); + g.setItem(10, mkItem(Material.PAPER, "§e§l头部", "§7" + brief(tplHeader), "", "§a点击编辑")); + g.setItem(11, mkItem(Material.PAPER, "§e§l商品行", "§7" + brief(tplItemLine), "", "§a点击编辑")); + g.setItem(12, mkItem(Material.PAPER, "§e§l优惠行", "§7" + brief(tplDiscountLine), "", "§a点击编辑")); + g.setItem(14, mkItem(Material.PAPER, "§e§l打包行", "§7" + brief(tplPackLine), "", "§a点击编辑")); + g.setItem(15, mkItem(Material.PAPER, "§e§l汇总", "§7" + brief(tplSummary), "", "§a点击编辑")); + g.setItem(16, mkItem(Material.PAPER, "§e§l页脚", "§7" + brief(tplFooter), "", "§a点击编辑")); + g.setItem(22, mkItem(Material.BOOK, "§6§l预览(随机价格)", "§7预览小票效果")); + g.setItem(48, mkItem(Material.LIME_WOOL, "§a§l保存")); g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(50, mkItem(Material.RED_WOOL, "§c§l重置")); + p.openInventory(g); + } + public void openReceiptBuilder(Player p, String section) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l编辑: " + sectionName(section)); + g.setItem(10, mkItem(Material.EMERALD, "§a{品名}")); g.setItem(11, mkItem(Material.EMERALD, "§a{原价}")); + g.setItem(12, mkItem(Material.EMERALD, "§a{执行价}")); g.setItem(13, mkItem(Material.EMERALD, "§a{数量}")); + g.setItem(14, mkItem(Material.EMERALD, "§a{小计}")); g.setItem(15, mkItem(Material.EMERALD, "§a{商店名}")); + g.setItem(16, mkItem(Material.EMERALD, "§a{玩家}")); g.setItem(19, mkItem(Material.DIAMOND, "§b{订单号}")); + g.setItem(20, mkItem(Material.DIAMOND, "§b{订单时间}")); g.setItem(21, mkItem(Material.DIAMOND, "§b{打印时间}")); + g.setItem(22, mkItem(Material.DIAMOND, "§b{日期}")); g.setItem(23, mkItem(Material.DIAMOND, "§b{时间}")); + g.setItem(24, mkItem(Material.DIAMOND, "§b{原价合计}")); g.setItem(25, mkItem(Material.DIAMOND, "§b{实付}")); + g.setItem(28, mkItem(Material.GOLD_INGOT, "§6{优惠额}")); g.setItem(29, mkItem(Material.GOLD_INGOT, "§6{优惠类型}")); + g.setItem(30, mkItem(Material.GOLD_INGOT, "§6{优惠券码}")); g.setItem(31, mkItem(Material.GOLD_INGOT, "§6{打包费}")); + g.setItem(32, mkItem(Material.GOLD_INGOT, "§6{打包颜色}")); g.setItem(33, mkItem(Material.GOLD_INGOT, "§6{余额}")); + g.setItem(37, mkItem(Material.STONE, "§f═══")); g.setItem(38, mkItem(Material.STONE, "§f───")); + g.setItem(39, mkItem(Material.STONE, "§f═════════════")); g.setItem(40, mkItem(Material.PAPER, "§e换行(\\n)")); + g.setItem(47, mkItem(Material.RED_STAINED_GLASS_PANE, "§c删末尾")); g.setItem(48, mkItem(Material.RED_WOOL, "§c清空")); + g.setItem(49, mkItem(Material.ARROW, "§7返回")); g.setItem(50, mkItem(Material.LIME_WOOL, "§a保存")); + g.setItem(51, mkItem(Material.BLUE_WOOL, "§9重置")); + p.openInventory(g); + } + // ===== 点击处理 ===== + public boolean handleCenterClick(Player p, int raw) { + if (raw == 13) { openMyOrders(p, 0); return true; } + if (raw == 31 && isAdmin(p)) { openAdminPanel(p, 0); return true; } + if (raw == 33 && isAdmin(p)) { openReceiptConfig(p); return true; } + if (raw == 49) { plugin.getShopManager().openShopMain(p); return true; } + return true; + } + public boolean handleMyOrdersClick(Player p, int raw, boolean left, boolean shift) { + List list = getPlayerAllOrders(p.getUniqueId()); + int page = userViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openMyOrders(p, page - 1); return true; } + if (raw == 50) { openMyOrders(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (left) { + try { ItemStack book = createReceiptBook(r); + HashMap ov = p.getInventory().addItem(book); + for (ItemStack d : ov.values()) p.getWorld().dropItemNaturally(p.getLocation(), d); + r.printTime = System.currentTimeMillis(); p.sendMessage("§a已补打小票书: #" + r.orderId); + } catch (Exception e) { p.sendMessage("§c补打失败"); } + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // ★ Shift+右键:仅退款申请(需管理员审批) + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请,等待管理员审批"); + notifyAdmins(p, r); + } else { + // ★ 普通右键:5分钟内直接退(自动通过) + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason + + ",Shift+右键可申请仅退款"); + } else { + // 扣货 + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + if (isShulker) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory() + .getItem(i); + if (is != null && is.getType() + .name().contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + // 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + } + + + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // 仅退款申请 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } else { + // 5分钟内退货退款 + String reason = validateRefund(r, p); + if (reason != null) { + // ★ 不满足条件:询问是否申请 + p.sendMessage("§c" + reason); + askReturnConfirm(p, r); + } else { + // 满足条件:自动退 + doAutoReturn(p, r); + } + } + } else { + if (shift) { + // 申请仅退款 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请"); + notifyAdmins(p, r); + } else { + // ★ 申请退货退款:先检查条件 + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason); + } + // ★ 无论是否满足条件都询问 + askReturnConfirm(p, r); + } + } + } + } + if (left) { + // 已退款订单仍然允许补打小票,但小票内会显示退款标记 + try { + ItemStack book = createReceiptBook(r); + HashMap overflow = + p.getInventory().addItem(book); + for (ItemStack drop : overflow.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + r.printTime = System.currentTimeMillis(); + if (r.status != 0) { + p.sendMessage("§e已补打小票(含退款标记): #" + + r.orderId); + } else { + p.sendMessage("§a已补打小票书: #" + + r.orderId); + } + } catch (Exception e) { + p.sendMessage("§c补打失败"); + } + } + + final int fp = page; Bukkit.getScheduler().runTask(plugin, () -> openMyOrders(p, fp)); + return true; + } + public boolean handleAdminClick(Player p, int raw) { + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openAdminPanel(p, page - 1); return true; } + if (raw == 50) { openAdminPanel(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= online.length) return true; + openAdminOrders(p, online[actual].getUniqueId(), 0); return true; + } + public boolean handleAdminOrdersClick(Player p, int raw, boolean left, boolean shift) { + UUID tu = adminViewTarget.get(p.getUniqueId()); + if (tu == null) return false; + List list = getPlayerOrdersWithin(tu, RETENTION_30D); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { + openAdminOrders(p, tu, page - 1); + return true; + } + if (raw == 50) { + openAdminOrders(p, tu, page + 1); + return true; + } + if (raw == 53) { + openAdminPanel(p, 0); + return true; + } + int si = slotIndex(raw); + if (si < 0) return true; + int actual = si + page * PAGE_SIZE; + if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (r.status == 2 || r.status == 3 || r.status == 4) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + return true; + } + if (left) { + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款被拒绝"); + openAdminOrders(p, tu, page); + } else if (r.status == 0) { + partialRefundListening.add(p.getUniqueId()); + partialRefundTarget.put(p.getUniqueId(), r); + p.closeInventory(); + p.sendMessage("§e输入退款百分比(1-100): #" + r.orderId + " 实付" + r.totalPaid + "枚"); + } + } else { + if (r.status == 0 || r.status == 1) { + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a全额退款 §e" + r.player + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, "§a退款 §6" + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + } + } + // ★ 替换 handleAdminOrdersClick 中的审批逻辑 + if (left) { + // 左键 = 拒绝 + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款申请已被拒绝"); + openAdminOrders(p, tu, page); + } + } else { + // 右键 = 批准 + if (r.status == 1) { + String rt = r.refundType != null + ? r.refundType : ""; + + // ★ 退款退货申请 → 检查潜影盒 + if (rt.equals("refund_return_apply")) { + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 检查玩家背包里有没有带订单标签的盒子 + Player target = Bukkit.getPlayer(r.uuid); + if (target == null || !target.isOnline()) { + p.sendMessage("§c玩家不在线,无法执行退货退款"); + openAdminOrders(p, tu, page); + return true; + } + + boolean foundTagged = false; + boolean foundAny = false; + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is == null) continue; + if (!is.getType().name() + .contains("SHULKER")) continue; + foundAny = true; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + foundTagged = true; + // 收走盒子 + target.getInventory().clear(i); + break; + } + } + } + if (foundTagged) break; + } + + if (foundTagged) { + // ★ 有带标记的盒子 → 收走 + 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + + r.player + " §6" + + r.totalPaid + "枚(盒子已收走)"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + return true; + } else if (foundAny) { + // ★ 有盒子但没标记 → 等管理员决定 + pendingReturns.put(r.orderId, + new PendingReturn(r)); + p.closeInventory(); + p.sendMessage("§e§l[退货审批] 订单 #" + + r.orderId); + p.sendMessage("§7玩家背包有潜影盒但" + + "无订单标记,可能已被替换"); + p.sendMessage("§a输入 §e1 §a= 收走盒子并退款"); + p.sendMessage("§e输入 §e2 §e= 仅退款(不收盒子)"); + p.sendMessage("§c输入 §c3 §c= 拒绝退款"); + p.sendMessage("§72分钟内未操作自动拒绝"); + // 启动2分钟超时 + startReturnTimeout(r.orderId); + return true; + } else { + // ★ 背包里没有任何盒子 + p.sendMessage("§c玩家背包里没有潜影盒"); + p.sendMessage("§7可拒绝并通知玩家" + + "申请仅退款或把货拿回来"); + openAdminOrders(p, tu, page); + return true; + } + } + // ★ 散装退货退款:扣货+退钱 + Player target = Bukkit.getPlayer(r.uuid); + if (target != null && target.isOnline()) { + for (OrderItem it : r.items) + removeItems(target, it.mat, it.qty); + } + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + return true; + } + + // ★ 仅退款申请 / 5分钟内自动退款 + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已批准退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退款已批准,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + } + } + return true; + } + + /** + * 询问用户是否提交退货退款申请 + */ + private void askReturnConfirm(Player p, OrderRecord r) { + returnConfirmWaiting.put(p.getUniqueId(), r.orderId); + p.sendMessage(""); + p.sendMessage("§e§l[退货退款申请]"); + p.sendMessage("§7订单 #" + r.orderId + + " 实付" + r.totalPaid + "枚"); + p.sendMessage("§a输入 §e1 §a= 提交退货退款申请"); + p.sendMessage("§c输入 §e2 §c= 取消"); + p.sendMessage("§730秒内未操作自动取消"); + p.sendMessage(""); + + // 30秒超时 + final UUID uuid = p.getUniqueId(); + final long oid = r.orderId; + new BukkitRunnable() { + @Override + public void run() { + Long waiting = returnConfirmWaiting.get(uuid); + if (waiting != null && waiting == oid) { + returnConfirmWaiting.remove(uuid); + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§7退货退款申请已超时取消"); + } + } + } + }.runTaskLater(plugin, RETURN_CONFIRM_TIMEOUT); + } + + /** + * 执行5分钟内自动退货退款 + */ + private void doAutoReturn(Player p, OrderRecord r) { + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 找并收走带标签的盒子 + boolean found = false; + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + p.getInventory().clear(i); + found = true; + break; + } + } + } + if (found) break; + } + // 没找到标签盒子,收任意潜影盒 + if (!found) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + + + // ===== 2分钟超时 ===== + private void startReturnTimeout(long orderId) { + new BukkitRunnable() { + @Override + public void run() { + PendingReturn pr = pendingReturns + .remove(orderId); + if (pr == null) return; // 已处理 + Player admin = Bukkit.getPlayer(pr.playerUUID); + if (admin != null && admin.isOnline()) { + admin.sendMessage("§c[退货审批] 订单 #" + + orderId + " 超时,自动拒绝"); + } + // 通知玩家 + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款申请已超时拒绝"); + // 恢复订单状态 + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 0; + r.refundType = null; + saveOrders(); + } + } + }.runTaskLater(plugin, 2400L); // 2分钟 + } + + // ===== 查找订单 ===== + private OrderRecord findOrder(long orderId) { + synchronized (orders) { + for (OrderRecord r : orders) { + if (r.orderId == orderId) return r; + } + } + return null; + } + + + public boolean handleReceiptConfigClick(Player p, int raw) { + if (raw == 49) { openOrderCenter(p); return true; } + if (raw == 48) { saveReceiptConfig(); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 50) { createDefaultReceiptConfig(new File(plugin.getDataFolder(), "小票设置.txt")); loadReceiptConfig(); p.sendMessage("§e已重置"); openReceiptConfig(p); return true; } + if (raw == 22) { sendRandomPreview(p); return true; } + String sec = null; + if (raw == 10) sec = "header"; else if (raw == 11) sec = "itemLine"; else if (raw == 12) sec = "discountLine"; + else if (raw == 14) sec = "packLine"; else if (raw == 15) sec = "summary"; else if (raw == 16) sec = "footer"; + if (sec != null) { receiptBuilderSection.put(p.getUniqueId(), sec); openReceiptBuilder(p, sec); } + return true; + } + public boolean handleReceiptBuilderClick(Player p, int raw) { + String sec = receiptBuilderSection.get(p.getUniqueId()); if (sec == null) return false; + String token = getTokenBySlot(raw); + if (token != null) { String cur = getSection(sec); setSection(sec, cur.isEmpty() ? token : cur + token); openReceiptBuilder(p, sec); return true; } + if (raw == 47) { String c = getSection(sec); int lb = c.lastIndexOf('}'); if (lb >= 0) { int ls = c.lastIndexOf('{', lb); if (ls >= 0) setSection(sec, c.substring(0, ls)); } else setSection(sec, c.substring(0, Math.max(0, c.length() - 8))); openReceiptBuilder(p, sec); return true; } + if (raw == 48) { setSection(sec, ""); openReceiptBuilder(p, sec); return true; } + if (raw == 49) { receiptBuilderSection.remove(p.getUniqueId()); openReceiptConfig(p); return true; } + if (raw == 50) { saveReceiptConfig(); receiptBuilderSection.remove(p.getUniqueId()); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 51) { setSection(sec, getDefaultVal(sec)); openReceiptBuilder(p, sec); return true; } + return true; + } + + // ===== 聊天事件 ===== + @EventHandler + public void onAdminChat(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); UUID uuid = p.getUniqueId(); + // ★ 在 onAdminChat 方法开头添加 +// ===== 退货审批输入 ===== + for (Map.Entry entry : + new HashMap<>(pendingReturns).entrySet()) { + Long pendingOrderId = entry.getKey(); + PendingReturn pr = entry.getValue(); + if (pr.playerUUID.equals(p.getUniqueId()) + || isAdmin(p)) { + try { + int choice = Integer.parseInt( + e.getMessage().trim()); + pendingReturns.remove(pendingOrderId); + OrderRecord r = findOrder(pendingOrderId); + if (r == null) { + p.sendMessage("§c订单不存在"); + return; + } + + if (choice == 1) { + // ★ 收走盒子并退款 + Player target = Bukkit.getPlayer( + pr.playerUUID); + if (target != null && target.isOnline()) { + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + pendingOrderId)) { + target.getInventory().clear(i); + break; + } + } + } + break; + } + } + } + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(盒子已收走)"); + notifyPlayer(pr.playerUUID, + "§a[订单] 退货退款成功,§6" + + pr.totalPaid + "枚已退还"); + + } else if (choice == 2) { + // ★ 仅退款(不收盒子) + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§e已仅退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(未收盒子)"); + notifyPlayer(pr.playerUUID, + "§e[订单] 仅退款成功,§6" + + pr.totalPaid + "枚已退还" + + ",盒子无需退回"); + + } else if (choice == 3) { + // ★ 拒绝 + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款 #" + pendingOrderId); + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款已被拒绝" + + ",您可以申请仅退款" + + "或把货拿回来后重新申请"); + } else { + p.sendMessage("§c请输入 1、2 或 3"); + pendingReturns.put(pendingOrderId, pr); + } + return; + + } catch (NumberFormatException ignored) { + // 不是数字,跳过 + } + } + } +// ★ 在 onAdminChat 方法开头,处理用户退货确认 + if (returnConfirmWaiting.containsKey(uuid)) { + String msg = e.getMessage().trim(); + if (msg.equals("1")) { + // ★ 确认提交 + Long orderId = returnConfirmWaiting.remove(uuid); + if (orderId != null) { + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 1; + r.refundType = "refund_return_apply"; + saveOrders(); + p.sendMessage("§e已提交退货退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } + } + e.setCancelled(true); + return; + } else if (msg.equals("2")) { + // ★ 取消 + returnConfirmWaiting.remove(uuid); + p.sendMessage("§7已取消退货退款申请"); + e.setCancelled(true); + return; + } else { + // 输入其他内容:取消等待 + returnConfirmWaiting.remove(uuid); + // 不 return,让消息正常发送 + } + } + + if (partialRefundListening.contains(uuid)) { + e.setCancelled(true); partialRefundListening.remove(uuid); + OrderRecord t = partialRefundTarget.remove(uuid); if (t == null) return; + try { int pct = Integer.parseInt(e.getMessage().trim()); if (pct < 1 || pct > 100) { p.sendMessage("§c1-100"); return; } + int ref = t.totalPaid * pct / 100; if (ref <= 0) ref = 1; + plugin.getBonds().addBonds(t.player, ref); t.status = 3; t.refundedAmount = ref; saveOrders(); + p.sendMessage("§a部分退款 §e" + t.player + " §6" + ref + "枚(" + pct + "%)"); + notifyPlayer(t.uuid, "§a退款 §6" + ref + "枚"); + final UUID tu = adminViewTarget.get(uuid); final int pg = adminViewPage.getOrDefault(uuid, 0); + Bukkit.getScheduler().runTask(plugin, () -> openAdminOrders(p, tu, pg)); + } catch (NumberFormatException ex) { p.sendMessage("§c请输入数字"); } catch (Exception ex) { plugin.getLogger().log(Level.WARNING, "[Order]退款异常", ex); } + return; + } + if (receiptBuilderSection.containsKey(uuid)) { + e.setCancelled(true); String sec = receiptBuilderSection.remove(uuid); if (sec == null) return; + String msg = e.getMessage().trim(); + if ("cancel".equalsIgnoreCase(msg)) { Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); return; } + setSection(sec, msg.replace("\\n", "\n")); p.sendMessage("§a已更新 §e" + sec); + Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); + } + } + + // ===== 数据查询 ===== + private List getPlayerAllOrders(UUID uuid) { + List r = new ArrayList<>(); + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && System.currentTimeMillis() - o.timestamp < RETENTION_30D) r.add(o); } + Collections.reverse(r); return r; + } + private List getPlayerOrdersWithin(UUID uuid, long ms) { + List r = new ArrayList<>(); long cut = System.currentTimeMillis() - ms; + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && o.timestamp >= cut) r.add(o); } + Collections.reverse(r); return r; + } + + // ===== 持久化 ===== + private void loadOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); if (!f.exists()) return; + try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); + OrderRecord cur = null; String line; + while ((line = br.readLine()) != null) { line = line.trim(); + if (line.equals("==")) { cur = new OrderRecord(); cur.items = new ArrayList<>(); } + else if (cur != null && line.contains("=")) { int eq = line.indexOf('='); String k = line.substring(0, eq); String v = line.substring(eq + 1); + switch (k) { case "id": cur.orderId = Long.parseLong(v); cur.timestamp = cur.orderId; break; case "uuid": cur.uuid = UUID.fromString(v); break; + case "player": cur.player = v; break; case "totalOriginal": cur.totalOriginal = Integer.parseInt(v); break; case "totalPaid": cur.totalPaid = Integer.parseInt(v); break; + case "discount": cur.discount = Integer.parseInt(v); break; case "discountType": cur.discountType = v; break; case "couponCode": cur.couponCode = v; break; + case "packFee": cur.packFee = Integer.parseInt(v); break; case "packType": cur.packType = v; break; case "packColor": cur.packColor = v; break; + case "status": cur.status = Integer.parseInt(v); break; case "refundedAmount": cur.refundedAmount = Integer.parseInt(v); break; + case "printTime": cur.printTime = Long.parseLong(v); break; case "refundType": cur.refundType = v; break; + case "item": String[] pp = v.split("\\|"); if (pp.length >= 5) cur.items.add(new OrderItem(pp[0], pp[1], Integer.parseInt(pp[2]), Integer.parseInt(pp[3]), Integer.parseInt(pp[4]))); break; } + } else if (line.isEmpty() && cur != null && cur.uuid != null) { orders.add(cur); cur = null; } + } if (cur != null && cur.uuid != null) orders.add(cur); br.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]加载失败: " + e.getMessage()); } + } + private void saveOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); + try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); + synchronized (orders) { for (OrderRecord r : orders) { pw.println("=="); + pw.println("id=" + r.orderId); pw.println("uuid=" + r.uuid); pw.println("player=" + r.player); + pw.println("totalOriginal=" + r.totalOriginal); pw.println("totalPaid=" + r.totalPaid); + pw.println("discount=" + r.discount); pw.println("discountType=" + nn(r.discountType)); + pw.println("couponCode=" + nn(r.couponCode)); pw.println("packFee=" + r.packFee); + pw.println("packType=" + nn(r.packType)); pw.println("packColor=" + nn(r.packColor)); + pw.println("status=" + r.status); pw.println("refundedAmount=" + r.refundedAmount); + pw.println("printTime=" + r.printTime); pw.println("refundType=" + nn(r.refundType)); + for (OrderItem it : r.items) pw.println("item=" + it.name + "|" + it.mat + "|" + it.originalPrice + "|" + it.finalPrice + "|" + it.qty); + } } pw.flush(); pw.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]保存失败: " + e.getMessage()); } + } + private void startCleanupTask() { new BukkitRunnable() { public void run() { synchronized (orders) { if (orders.removeIf(OrderManager.this::isExpired)) saveOrders(); } } }.runTaskTimer(plugin, 36000L, 36000L); } + /** + * 将小票书塞入潜影盒 + */ + public void addBookToShulker(ItemStack shulker, ItemStack book) { + try { + org.bukkit.inventory.meta.BlockStateMeta bm = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (bm == null) return; + org.bukkit.block.BlockState st = bm.getBlockState(); + if (st instanceof org.bukkit.block.Container) { + ((org.bukkit.block.Container) st) + .getInventory().addItem(book); + bm.setBlockState(st); + shulker.setItemMeta(bm); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Order] 塞书失败: " + e.getMessage()); + } + } + + // ===== 工具 ===== + private String nn(String s) { return s != null ? s : ""; } + private boolean isExpired(OrderRecord r) { return System.currentTimeMillis() - r.timestamp > RETENTION_30D; } + private String fmtDate(long ts) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(ts)); } + private int slotIndex(int raw) { for (int i = 0; i < SLOTS.length; i++) if (SLOTS[i] == raw) return i; return -1; } + private String brief(String s) { if (s == null || s.isEmpty()) return "(空)"; String c = sc(s); return c.length() > 30 ? c.substring(0, 30) + "..." : c; } + private String sectionName(String s) { switch(s){case "header":return "头部";case "itemLine":return "商品行";case "discountLine":return "优惠行";case "packLine":return "打包行";case "summary":return "汇总";case "footer":return "页脚";default:return s;} } + private String sc(String s) { + if (s == null) return ""; + return s.replaceAll("§[0-9a-fk-orA-FK-OR]", ""); + } + private String getSection(String s) { switch(s){case "header":return tplHeader;case "itemLine":return tplItemLine;case "discountLine":return tplDiscountLine;case "packLine":return tplPackLine;case "summary":return tplSummary;case "footer":return tplFooter;default:return "";} } + private void setSection(String s, String v) { switch(s){case "header":tplHeader=v;break;case "itemLine":tplItemLine=v;break;case "discountLine":tplDiscountLine=v;break;case "packLine":tplPackLine=v;break;case "summary":tplSummary=v;break;case "footer":tplFooter=v;break;} } + private String getDefaultVal(String s) { switch(s){case "header":return "§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}";case "itemLine":return "§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚";case "discountLine":return "优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})";case "packLine":return "打包: §c+{打包费}枚§r ({打包颜色})";case "summary":return "原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚";case "footer":return "感谢惠顾,欢迎再来!";default:return "";} } + private String getTokenBySlot(int raw) { switch(raw){case 10:return "{品名}";case 11:return "{原价}";case 12:return "{执行价}";case 13:return "{数量}";case 14:return "{小计}";case 15:return "{商店名}";case 16:return "{玩家}";case 19:return "{订单号}";case 20:return "{订单时间}";case 21:return "{打印时间}";case 22:return "{日期}";case 23:return "{时间}";case 24:return "{原价合计}";case 25:return "{实付}";case 28:return "{优惠额}";case 29:return "{优惠类型}";case 30:return "{优惠券码}";case 31:return "{打包费}";case 32:return "{打包颜色}";case 33:return "{余额}";case 37:return "═══";case 38:return "───";case 39:return "═════════════";case 40:return "\\n";default:return null;} } + private ItemStack mkItem(Material mat, String name, String... lore) { ItemStack it = new ItemStack(mat); ItemMeta im = it.getItemMeta(); if (im != null) { im.setDisplayName(name); if (lore.length > 0) im.setLore(Arrays.asList(lore)); it.setItemMeta(im); } return it; } + private Map loadMap(File f) { Map m = new LinkedHashMap<>(); try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); String line; while ((line = br.readLine()) != null) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) continue; int eq = line.indexOf('='); if (eq > 0) m.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); } br.close(); } catch (Exception ignored) {} return m; } + private void writeLines(File f, List lines) { try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); for (String l : lines) pw.println(l); pw.flush(); pw.close(); } catch (Exception ignored) {} } + private void sendRandomPreview(Player p) { String[] n = {"钻石剑","钻石镐","铁剑","金苹果"}; String[] m = {"DIAMOND_SWORD","DIAMOND_PICKAXE","IRON_SWORD","GOLDEN_APPLE"}; Random r = new Random(); List items = new ArrayList<>(); for (int i = 0; i < 2 + r.nextInt(3); i++) { int idx = r.nextInt(n.length); items.add(new OrderItem(n[idx], m[idx], 100 + r.nextInt(5000), 100 + r.nextInt(5000), 1 + r.nextInt(10))); } OrderRecord o = new OrderRecord(); o.orderId = System.currentTimeMillis(); o.uuid = p.getUniqueId(); o.player = p.getName(); o.items = items; o.totalOriginal = 0; for (OrderItem it : items) o.totalOriginal += it.originalSubtotal(); o.totalPaid = o.totalOriginal; o.timestamp = o.orderId; o.status = 0; p.sendMessage("§6§l===== 小票预览(随机) ====="); sendReceiptChat(p, o); p.sendMessage("§6§l===== 预览结束 ====="); } +} diff --git a/backup/src/main/java/Sdf1_login/PVPManager.java b/backup/src/main/java/Sdf1_login/PVPManager.java new file mode 100644 index 0000000..326c0fb --- /dev/null +++ b/backup/src/main/java/Sdf1_login/PVPManager.java @@ -0,0 +1,1380 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.scoreboard.*; + + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class PVPManager implements Listener { + + private final Main plugin; + private Connection pvpDb; + + private final Map + regions = new ConcurrentHashMap<>(); + private final Map + inRegion = new ConcurrentHashMap<>(); + private final Map + sessionKills = + new ConcurrentHashMap<>(); + private final Map + lastKillTime = + new ConcurrentHashMap<>(); + private final Map + selectState = + new ConcurrentHashMap<>(); + private final Map + killSessions = + new ConcurrentHashMap<>(); + private final Set tripleAnnounced = + ConcurrentHashMap.newKeySet(); + private final Map lastScoreboardRefresh = new ConcurrentHashMap<>(); + + + // 边框显示 + private final Set + permanentBorders = + ConcurrentHashMap.newKeySet(); + private final Set + tempBorders = + ConcurrentHashMap.newKeySet(); + + + public static class KillSession { + public int count = 0; + public long firstKill = 0; + public long lastKill = 0; + public boolean tripleAnnounced = + false; + public String region = ""; + } + + public static class PVPRegion { + public String name; + public String world; + public int x1, y1, z1; + public int x2, y2, z2; + public int minX, minY, minZ; + public int maxX, maxY, maxZ; + + public void calcBounds() { + minX = Math.min(x1, x2); + minY = Math.min(y1, y2); + minZ = Math.min(z1, z2); + maxX = Math.max(x1, x2); + maxY = Math.max(y1, y2); + maxZ = Math.max(z1, z2); + } + + public boolean contains( + Location loc) { + if (loc == null) return false; + if (!loc.getWorld().getName() + .equals(world)) + return false; + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + return x >= minX && x <= maxX + && y >= minY && y <= maxY + && z >= minZ && z <= maxZ; + } + } + + public PVPManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadRegions(); + } + + // ===== 独立数据库 ===== + + private void initDB() { + try { + Class.forName( + "org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), + "pvp.db"); + pvpDb = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = + pvpDb.createStatement(); + st.execute( + "PRAGMA journal_mode=WAL"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "name TEXT NOT NULL " + + "UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER " + + "DEFAULT 0)"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT " + + "NOT NULL," + + "region_name TEXT " + + "NOT NULL," + + "kills INTEGER " + + "DEFAULT 0," + + "deaths INTEGER " + + "DEFAULT 0," + + "PRIMARY KEY" + + "(player_name, " + + "region_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[PVP] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== DB 操作 ===== + + private void saveRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions" + + " (name, world," + + " x1,y1,z1," + + " x2,y2,z2," + + " create_time)" + + " VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Map + getRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + private List> + getAllRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + pvpDb.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + private void deleteRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private int getKills(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("kills"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private int getDeaths(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("deaths"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private void addKill(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,1,0) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET kills=" + + "kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void addDeath(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,0,1) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET deaths=" + + "deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private List> + getTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT player_name," + + " kills,deaths" + + " FROM " + + "pvp_kills " + + "WHERE " + + "region_name=?" + + " AND kills>0" + + " ORDER BY kills" + + " DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = + ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString( + "player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Set getRegionNames() { + return regions.keySet(); + } + + // ===== 加载 ===== + + private void loadRegions() { + regions.clear(); + List> list = + getAllRegions(); + for (Map row : list) { + PVPRegion r = new PVPRegion(); + r.name = (String) row + .get("name"); + r.world = (String) row + .get("world"); + r.x1 = ((Number) row + .get("x1")).intValue(); + r.y1 = ((Number) row + .get("y1")).intValue(); + r.z1 = ((Number) row + .get("z1")).intValue(); + r.x2 = ((Number) row + .get("x2")).intValue(); + r.y2 = ((Number) row + .get("y2")).intValue(); + r.z2 = ((Number) row + .get("z2")).intValue(); + r.calcBounds(); + regions.put(r.name, r); + } + } + + // ===== 工具 ===== + + public ItemStack createTool() { + ItemStack tool = new ItemStack( + Material.BLAZE_ROD); + ItemMeta m = tool.getItemMeta(); + m.setDisplayName( + "§6§l[PVP] 区域选择工具"); + m.setLore(Arrays.asList( + "§7右键:设置第一个角", + "§7左键:设置第二个角")); + m.getPersistentDataContainer() + .set(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING, + "tool"); + tool.setItemMeta(m); + return tool; + } + + private boolean isPVPTool( + ItemStack item) { + if (item == null) return false; + ItemMeta m = item.getItemMeta(); + if (m == null) return false; + return m.getPersistentDataContainer() + .has(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING); + } + + public void giveTool(Player p) { + boolean has = false; + for (ItemStack it : p.getInventory() + .getContents()) { + if (isPVPTool(it)) { + has = true; + break; + } + } + if (!has) { + p.getInventory() + .addItem(createTool()); + } + } + + // ===== 边框粒子 ===== + + private void drawBorder(Player target, + PVPRegion r) { + if (r == null) return; + World w = Bukkit.getWorld(r.world); + if (w == null) return; + int step = 2; + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.maxX, r.minY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.minY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.minX, r.minY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.minY, r.minZ, step); + drawLine(target, w, + r.minX, r.maxY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.minZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.maxY, r.maxZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + } + + private void drawLine(Player target, + World w, + int x1, int y1, int z1, + int x2, int y2, int z2, + int step) { + double dx = x2 - x1; + double dy = y2 - y1; + double dz = z2 - z1; + double dist = Math.sqrt( + dx * dx + dy * dy + dz * dz); + int steps = (int) (dist / step); + if (steps < 1) steps = 1; + for (int i = 0; i <= steps; i++) { + double t = (double) i / steps; + Location loc = new Location(w, + x1 + dx * t, + y1 + dy * t + 0.1, + z1 + dz * t); + w.spawnParticle( + Particle.FLAME, loc, 1, + 0.0, 0.0, 0.0, 0.0); + } + } + + // ===== 每秒 tick ===== + + public void tickRegions() { + long now = + System.currentTimeMillis(); + + // 玩家区域检测 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pn = p.getName(); + Location loc = p.getLocation(); + String inside = null; + for (PVPRegion r : + regions.values()) { + if (r.contains(loc)) { + inside = r.name; + break; + } + } + String was = inRegion.get(pn); + + if (inside != null && !inside.equals(was)) { + inRegion.put(pn, inside); + if (was == null) { + String reg = inside; + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> + updateScoreboard( + pp, reg), + 1L); + } + + } else if (inside == null + && was != null) { + inRegion.remove(pn); + // 不删 sessionKills,等倒计时结束再清 + removeScoreboard(p); + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> plugin + .giveMenuSnowball(pp), + 5L); + } + + } + + // 5分钟超时重置连杀session + for (String pname : + new ArrayList<>( + killSessions + .keySet())) { + KillSession ks = + killSessions.get(pname); + if (ks == null) continue; + if (now - ks.lastKill + > 5 * 60 * 1000) { + killSessions.remove(pname); + } + } + + // 5分钟无击杀 → 计数榜切总榜 + // 5分钟无击杀 → 归档并清零 + for (String rName : + new ArrayList<>( + lastKillTime + .keySet())) { + long last = lastKillTime + .getOrDefault(rName, 0L); + if (now - last > 5 * 60 * 1000) { + // 清零该区域所有sessionKills + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + rName + ":")); + // 切换到总榜 + refreshScoreboard(rName); + lastKillTime.remove(rName); + lastScoreboardRefresh.remove(rName); + + } + } + + + // 边框渲染 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (pr == null) continue; + PVPRegion r = regions.get(pr); + if (r == null) continue; + if (permanentBorders + .contains(pr)) { + drawBorder(p, r); + } + if (tempBorders + .contains(pr)) { + drawBorder(p, r); + } + // 每秒刷新活跃区域的计分板倒计时 + for (String rName : new ArrayList<>(lastKillTime.keySet())) { + long last = lastScoreboardRefresh.getOrDefault(rName, 0L); + if (now - last >= 1000) { + lastScoreboardRefresh.put(rName, now); + for (Player tp : Bukkit.getOnlinePlayers()) { + String inR = inRegion.get(tp.getName()); + if (rName.equals(inR)) { + showCounter(tp, rName); + } + } + } + } + + + + } + } + + private void broadcastWithSound(String msg, Sound sound) { + Bukkit.broadcastMessage(msg); + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), sound, 1.0f, 1.0f); + } + } + + + private void initSession(String player, + String region) { + killSessions.remove(player); + tripleAnnounced.remove( + region + ":" + player); + KillSession s = new KillSession(); + s.region = region; + s.firstKill = + System.currentTimeMillis(); + s.lastKill = + System.currentTimeMillis(); + killSessions.put(player, s); + } + + // ===== 击杀事件 ===== + + @EventHandler + public void onDeath( + EntityDeathEvent event) { + if (!(event.getEntity() + instanceof Player)) return; + Player victim = + (Player) event.getEntity(); + Player killer = + victim.getKiller(); + if (killer == null) return; + if (killer.equals(victim)) return; + + String region = + inRegion.get( + victim.getName()); + if (region == null) return; + + String vName = victim.getName(); + String kName = killer.getName(); + + // 总榜 + addKill(kName, region); + addDeath(vName, region); + + // 计数榜 + String kKey = + region + ":" + kName; + sessionKills.merge(kKey, 1, + Integer::sum); + lastKillTime.put(region, + System.currentTimeMillis()); + + // 终结检测 + // 终结检测 + KillSession victimSession = + killSessions.get(vName); + if (victimSession != null + && victimSession.count >= 3) { + String endMsg = + "§b§l" + kName + + " §e终结了 " + + vName + " §e的 " + + victimSession.count + + "连杀!§a恭喜终结者 " + + kName + + "§e!§7快来挑战!"; + killSessions.remove(vName); + broadcastWithSound(endMsg, Sound.ENTITY_ENDER_DRAGON_GROWL); + + killSessions.remove(vName); + } + + + // 击杀者连杀 + KillSession ks = + killSessions.get(kName); + if (ks == null + || !ks.region.equals(region) + || System.currentTimeMillis() + - ks.lastKill > 5 * 60 * 1000) { + tripleAnnounced.remove( + region + ":" + kName); + KillSession ns = + new KillSession(); + ns.region = region; + ns.count = 1; + ns.firstKill = + System.currentTimeMillis(); + ns.lastKill = + System.currentTimeMillis(); + killSessions.put(kName, ns); + } else { + ks.count++; + ks.lastKill = + System.currentTimeMillis(); + } + + // 多杀通报 + KillSession killerSession = + killSessions.get(kName); + if (killerSession != null) { + plugin.getLogger().info( + "[PVP] checkMultiKill: " + + kName + " count=" + + killerSession.count + + " region=" + region); + checkMultiKill(killer, + region, killerSession); + } + + // 刷新计分板 + for (Player p : + Bukkit.getOnlinePlayers()) { + if (region.equals( + inRegion.get( + p.getName()))) { + updateScoreboard(p, region); + } + } + + victim.sendMessage("§c你在「" + + region + "」被 " + + kName + " 击杀"); + killer.sendMessage("§a你在「" + + region + "」击杀了 " + + vName); + } + + private void checkMultiKill( + Player killer, String region, + KillSession session) { + int count = session.count; + String kName = killer.getName(); + String key = region + ":" + kName; + String msg = null; + + if (count >= 5) { + msg = "§c§l" + kName + + " §e在「" + region + + "」达成 " + count + + "连杀!§7§l快来挑战!"; + } else if (count == 3) { + if (tripleAnnounced.add(key)) { + msg = "§6§l三杀!" + + kName + + " 势不可挡!" + + "§7快来挑战!"; + } + } + + if (msg != null) { + if (count >= 5) { + broadcastWithSound(msg, Sound.ENTITY_ENDER_DRAGON_GROWL); + } else { + broadcastWithSound(msg, Sound.BLOCK_END_PORTAL_SPAWN); + } + } + + } + + + + // ===== 计分板(英文ID) ===== + + private void ensureScoreboard( + Player p, String region) { + // 活跃击杀:每秒强制刷新(更新倒计时) + if (lastKillTime.containsKey(region)) { + showCounter(p, region); + return; + } + // 无活跃击杀:只在没有scoreboard时创建总榜 + Scoreboard sb = p.getScoreboard(); + if (sb == null + || sb.getObjective( + "sdf1pvp") == null) { + showTotal(p, region); + } + } + + + private void updateScoreboard( + Player p, String region) { + if (lastKillTime + .containsKey(region)) { + showCounter(p, region); + } else { + showTotal(p, region); + } + } + + private void showTotal(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = + mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§6§l击杀总榜 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List> top = + getTop(region, 10); + if (top.isEmpty()) { + obj.getScore("§7暂无记录") + .setScore(0); + } else { + int rank = 10; + for (Map row : + top) { + String name = (String) row + .get("player_name"); + int kills = ((Number) row + .get("kills")) + .intValue(); + int deaths = ((Number) row + .get("deaths")) + .intValue(); + String line = + "§e#" + (11 - rank) + + " §f" + name + + " §a" + kills + + "§7/§c" + deaths; + obj.getScore(line) + .setScore(rank); + rank--; + } + } + p.setScoreboard(sb); + } + private void showCounter(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§c§l实时战况 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List entries = + new ArrayList<>(); + for (Map.Entry e : + sessionKills.entrySet()) { + if (!e.getKey().startsWith( + region + ":")) + continue; + if (e.getValue() <= 0) continue; + String pName = e.getKey() + .substring( + region.length() + 1); + entries.add(new String[]{ + pName, + String.valueOf( + e.getValue())}); + } + + if (entries.isEmpty()) { + obj.getScore("§7等待击杀...") + .setScore(1); + } else { + entries.sort((a, b) -> + Integer.compare( + Integer.parseInt(b[1]), + Integer.parseInt(a[1]))); + int rank = 10; + for (String[] entry : entries) { + obj.getScore( + "§e" + entry[0] + + " §a" + entry[1]) + .setScore(rank); + rank--; + if (rank < 1) break; + } + } + + Long last = + lastKillTime.get(region); + if (last != null) { + long elapsed = + System.currentTimeMillis() + - last; + long remaining = + Math.max(0, + 5 * 60 * 1000 - elapsed); + long secs = remaining / 1000; + obj.getScore( + "§7冷却: " + secs + "s") + .setScore(-1); + } + + // 关键:强制赋值新scoreboard + p.setScoreboard(sb); + } + + private void removeScoreboard( + Player p) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + p.setScoreboard( + mgr.getNewScoreboard()); + } + + private void refreshScoreboard( + String regionName) { + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (regionName.equals(pr)) { + showTotal(p, regionName); + } + } + } + + // ===== 工具选择事件 ===== + + @EventHandler + public void onInteract( + PlayerInteractEvent event) { + Player p = event.getPlayer(); + ItemStack hand = + p.getInventory() + .getItemInMainHand(); + if (!isPVPTool(hand)) return; + + if (!event.getAction() + .isRightClick() + && !event.getAction() + .isLeftClick()) return; + + Location loc = p.getLocation(); + String[] state = + selectState.get(p.getName()); + if (state == null) { + state = new String[]{ + loc.getWorld() + .getName(), + "", ""}; + selectState.put( + p.getName(), state); + } + + String coord = + loc.getBlockX() + "," + + loc.getBlockY() + "," + + loc.getBlockZ(); + + if (event.getAction() + .isRightClick()) { + state[0] = loc.getWorld() + .getName(); + state[1] = coord; + p.sendMessage( + "§a第一个角: " + coord); + } else { + state[2] = coord; + p.sendMessage( + "§a第二个角: " + coord); + } + event.setCancelled(true); + } + + // ===== 退出清理 ===== + + @EventHandler + public void onQuit( + PlayerQuitEvent event) { + String name = + event.getPlayer().getName(); + inRegion.remove(name); + selectState.remove(name); + } + + // ===== 指令 ===== + + public boolean onCommand(Player p, + String[] args) { + if (args.length == 0) { + return showStats(p, + p.getName()); + } + switch (args[0].toLowerCase()) { + case "create": + return handleCreate( + p, args); + case "stats": + if (args.length >= 2) + return showStats(p, + args[1]); + return showStats(p, + p.getName()); + case "list": + return handleList(p); + case "delete": + if (args.length >= 2) + return handleDelete(p, + args[1]); + p.sendMessage( + "§c用法: " + + "/pvp delete <名字>"); + return true; + case "tool": + giveTool(p); + p.sendMessage( + "§a已发放" + + "PVP区域选择工具"); + return true; + case "on": + return handleBorderOn( + p, args); + case "off": + return handleBorderOff( + p, args); + case "tempban": + return handleTempBan( + p, args); + default: + return showStats(p, + p.getName()); + } + } + + private boolean handleBorderOn( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp on <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + permanentBorders.add(name); + p.sendMessage("§a已开启「" + + name + "」永久边框显示"); + return true; + } + + private boolean handleBorderOff( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp off <区域名>"); + return true; + } + permanentBorders.remove(name); + tempBorders.remove(name); + p.sendMessage("§a已关闭「" + + name + "」边框显示"); + return true; + } + + private boolean handleTempBan( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp tempban <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + tempBorders.add(name); + p.sendMessage("§a「" + name + + "」临时边框显示15秒"); + final String rName = name; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> + tempBorders + .remove(rName), + 300L); + return true; + } + + private boolean handleCreate( + Player p, String[] args) { + if (args.length >= 8) { + try { + int x1 = Integer.parseInt( + args[1]); + int y1 = Integer.parseInt( + args[2]); + int z1 = Integer.parseInt( + args[3]); + int x2 = Integer.parseInt( + args[4]); + int y2 = Integer.parseInt( + args[5]); + int z2 = Integer.parseInt( + args[6]); + String rName = args[7]; + return createRegion(p, + rName, + p.getWorld() + .getName(), + x1, y1, z1, + x2, y2, z2); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c坐标格式错误"); + return true; + } + } + + if (args.length >= 2) { + String[] state = + selectState + .get(p.getName()); + if (state == null + || state[1].isEmpty() + || state[2].isEmpty()) { + p.sendMessage( + "§c请先用工具选择两个角"); + return true; + } + String[] p1 = + state[1].split(","); + String[] p2 = + state[2].split(","); + return createRegion(p, + args[1], state[0], + Integer.parseInt(p1[0]), + Integer.parseInt(p1[1]), + Integer.parseInt(p1[2]), + Integer.parseInt(p2[0]), + Integer.parseInt(p2[1]), + Integer.parseInt(p2[2])); + } + + p.sendMessage( + "§c用法: /pvp create " + + " " + + " <名字>"); + p.sendMessage( + "§c或: /pvp create " + + "<名字> (先用工具选择)"); + return true; + } + + private boolean createRegion( + Player p, String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + if (getRegion(name) != null) { + p.sendMessage( + "§c区域已存在: " + name); + return true; + } + PVPRegion r = new PVPRegion(); + r.name = name; + r.world = world; + r.x1 = x1; + r.y1 = y1; + r.z1 = z1; + r.x2 = x2; + r.y2 = y2; + r.z2 = z2; + r.calcBounds(); + saveRegion(name, world, + x1, y1, z1, + x2, y2, z2); + regions.put(name, r); + p.sendMessage( + "§a§lPVP区域创建成功!"); + p.sendMessage( + "§7名称: §f" + name); + p.sendMessage("§7范围: §f" + + r.minX + "," + r.minY + + "," + r.minZ + " → " + + r.maxX + "," + r.maxY + + "," + r.maxZ); + selectState.remove(p.getName()); + return true; + } + + private boolean handleList( + Player p) { + List> all = + getAllRegions(); + if (all.isEmpty()) { + p.sendMessage( + "§7暂无PVP区域"); + return true; + } + p.sendMessage( + "§6§lPVP区域列表:"); + for (Map row : + all) { + p.sendMessage("§e- §f" + + row.get("name") + + " §7(" + row.get("world") + + ")"); + } + return true; + } + + private boolean handleDelete( + Player p, String name) { + if (getRegion(name) == null) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + deleteRegion(name); + regions.remove(name); + permanentBorders.remove(name); + tempBorders.remove(name); + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + name + ":")); + lastKillTime.remove(name); + lastScoreboardRefresh.remove(name); + p.sendMessage( + "§a已删除: " + name); + return true; + } + + private boolean showStats(Player p, + String target) { + String region = + inRegion.get(target); + if (region == null) { + p.sendMessage("§6§l" + target + + " 的PVP战绩:"); + List> all = + getAllRegions(); + boolean has = false; + for (Map row : + all) { + String rn = (String) row + .get("name"); + int kills = + getKills(target, rn); + int deaths = + getDeaths(target, rn); + if (kills > 0 + || deaths > 0) { + p.sendMessage( + "§e- §f" + rn + + " §a击杀:" + kills + + " §c死亡:" + deaths); + has = true; + } + } + if (!has) + p.sendMessage( + "§7暂无记录"); + } else { + int kills = + getKills(target, region); + int deaths = + getDeaths(target, region); + double kd = deaths > 0 + ? (double) kills / deaths + : kills; + p.sendMessage("§6§l" + target + + " @ " + region); + p.sendMessage( + "§a击杀: " + kills + + " §c死亡: " + deaths + + " §eKD: " + + String.format( + "%.2f", kd)); + } + return true; + } +} diff --git a/backup/src/main/java/Sdf1_login/PasswordUtils.java b/backup/src/main/java/Sdf1_login/PasswordUtils.java new file mode 100644 index 0000000..4029ed4 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/PasswordUtils.java @@ -0,0 +1,72 @@ +package Sdf1_login; + +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Base64; + +public class PasswordUtils { + + private static final SecureRandom RANDOM = + new SecureRandom(); + + public static String generateSalt() { + byte[] salt = new byte[16]; + RANDOM.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + public static String hash(String password, String salt) { + try { + MessageDigest md = + MessageDigest.getInstance("SHA-256"); + md.update(Base64.getDecoder().decode(salt)); + byte[] hash = + md.digest(password.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(hash); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean validate(String password) { + if (password == null) return false; + if (password.length() < 6 || password.length() > 20) + return false; + boolean hasUpper = false; + boolean hasLower = false; + boolean hasDigit = false; + for (char c : password.toCharArray()) { + if (Character.isUpperCase(c)) hasUpper = true; + if (Character.isLowerCase(c)) hasLower = true; + if (Character.isDigit(c)) hasDigit = true; + } + return hasUpper && hasLower && hasDigit; + } + + public static String generateTempPassword() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(8); + for (int i = 0; i < 8; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateInviteCode() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(6); + for (int i = 0; i < 6; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateVerifyCode() { + int code = 100000 + RANDOM.nextInt(900000); + return String.valueOf(code); + } +} diff --git a/backup/src/main/java/Sdf1_login/PlayerStorage.java b/backup/src/main/java/Sdf1_login/PlayerStorage.java new file mode 100644 index 0000000..f93caa3 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/PlayerStorage.java @@ -0,0 +1,130 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.logging.Logger; + +public class PlayerStorage { + private Connection conn; + private final Logger logger; + + public PlayerStorage(File dataFolder, + Logger logger) { + this.logger = logger; + File dbFile = new File( + dataFolder, + "quest_data.db"); + try { + Class.forName( + "org.sqlite.JDBC"); + conn = DriverManager + .getConnection( + "jdbc:sqlite:" + + dbFile + .getAbsolutePath()); + conn.setAutoCommit(true); + Statement s = + conn.createStatement(); + s.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_kv (" + + "player TEXT NOT NULL," + + "key TEXT NOT NULL," + + "value TEXT," + + "PRIMARY KEY" + + "(player, key))"); + s.close(); + logger.info( + "[QuestStorage] 初始化: " + + dbFile.getAbsolutePath()); + } catch (Exception e) { + logger.severe( + "[QuestStorage] 失败: " + + e.getMessage()); + } + } + + public Object getCondition( + String player, String key) { + if (conn == null) return null; + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value " + + "FROM quest_kv " + + "WHERE player=? " + + "AND key=?"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ResultSet rs = + ps.executeQuery(); + String val = rs.next() + ? rs.getString("value") + : null; + rs.close(); + ps.close(); + return parseValue(val); + } catch (SQLException e) { + return null; + } + } + + public void setCondition( + String player, String key) { + setConditionRaw( + player, key, "true"); + } + public void debugPlayer(String player) { + + } + public void setConditionRaw( + String player, String key, + String value) { + if (conn == null) return; + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO quest_kv" + + "(player,key,value)" + + " VALUES(?,?,?)"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ps.setString(3, value); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + logger.warning( + "[QuestStorage] 写入失败: " + + key + "=" + value + + " | " + e.getMessage()); + } + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) + conn.close(); + } catch (Exception e) {} + } + + private Object parseValue(String v) { + if (v == null) return null; + try { + return Long.parseLong(v); + } catch (Exception e1) { + try { + return Double.parseDouble(v); + } catch (Exception e2) { + if ("true".equalsIgnoreCase(v)) + return true; + if ("false".equalsIgnoreCase(v)) + return false; + return v; + } + } + } +} diff --git a/backup/src/main/java/Sdf1_login/PointsManager.java b/backup/src/main/java/Sdf1_login/PointsManager.java new file mode 100644 index 0000000..77fc195 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/PointsManager.java @@ -0,0 +1,374 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + + +import java.lang.reflect.Method; +import java.util.*; + +public class PointsManager { + + private final Main plugin; + private Object cyInstance = null; + private Method cyActivationMethod = null; + private Method cyGetShopItemsMethod = null; + + private final List> cyItems = + new ArrayList<>(); + + public PointsManager(Main plugin) { + this.plugin = plugin; + discoverCY(); + } + + private void discoverCY() { + try { + org.bukkit.plugin.Plugin p = Bukkit + .getPluginManager() + .getPlugin("CY_beibao"); + if (p != null && p.isEnabled()) { + cyInstance = p; + cyGetShopItemsMethod = p.getClass() + .getMethod("onSdf1GetShopItems"); + cyActivationMethod = p.getClass() + .getMethod("onSdf1Activation", + String.class, + int.class, int.class); + plugin.getLogger().info( + "[Sdf1_login] CY_beibao 联控已连接"); + } + } catch (Exception ignored) { + } + } + + private void refreshCYItems() { + cyItems.clear(); + if (cyInstance == null + || cyGetShopItemsMethod == null) return; + try { + Object result = + cyGetShopItemsMethod.invoke(cyInstance); + if (result == null) return; + String data = result.toString(); + if (data.isEmpty()) return; + String[] items = data.split(";"); + for (String itemStr : items) { + String[] p = itemStr.split("\\|"); + if (p.length < 8) continue; + Map item = + new LinkedHashMap<>(); + item.put("id", p[0]); + item.put("name", p[1]); + item.put("days", + Integer.parseInt(p[2])); + item.put("slots", + Integer.parseInt(p[3])); + item.put("price", + Integer.parseInt(p[4])); + item.put("stock", + Integer.parseInt(p[5])); + item.put("icon", p[6]); + item.put("lifetime", + "1".equals(p[7])); + cyItems.add(item); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 拉取CY商品失败: " + + e.getMessage()); + } + } + + private void activateCY(String name, + int slots, int days) { + try { + if (cyInstance == null + || cyActivationMethod == null) return; + cyActivationMethod.invoke( + cyInstance, name, slots, days); + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] CY激活失败: " + + e.getMessage()); + } + } + + public int getPoints(String name) { + return ((Number) plugin.getDb() + .getField(name, "points")).intValue(); + } + + public Inventory createShopGUI(Player p) { + int points = getPoints(p.getName()); + Inventory inv = Bukkit.createInventory( + null, 54, + "§d§l积分商城 | 当前积分: " + points); + fillBg(inv); + inv.setItem(10, mkItem(Material.EMERALD, + "§a游戏币充值", + "§7消耗积分兑换游戏币", + "§7100积分 = $1000", + "", + "§e点击兑换")); + inv.setItem(12, mkItem(Material.ENDER_CHEST, + "§b背包空间", + "§7消耗积分兑换背包格子", + "§750积分 = 9格永久空间", + "", + "§e点击兑换")); + inv.setItem(14, mkItem(Material.EXPERIENCE_BOTTLE, + "§e经验瓶", + "§7消耗积分兑换经验", + "§720积分 = 10级经验", + "", + "§e点击兑换")); + if (cyInstance != null) { + inv.setItem(31, mkItem(Material.CHEST, + "§6背包商城", + "§7来自CY_beibao的商品", + "", + "§e点击查看")); + } + inv.setItem(49, mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public Inventory createCYShopGUI(Player p) { + refreshCYItems(); + int points = getPoints(p.getName()); + int size = Math.max(54, + ((cyItems.size() / 9) + 1) * 9); + if (size > 54) size = 54; + Inventory inv = Bukkit.createInventory( + null, size, + "§d§lCY背包商城 | 当前积分: " + points); + fillBg(inv); + for (int i = 0; i < cyItems.size() + && i < 45; i++) { + Map item = cyItems.get(i); + String name = (String) item.get("name"); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + List lore = new ArrayList<>(); + lore.add("§7价格: §e" + price + " 积分"); + if (lifetime) { + lore.add("§7格子: §e终身 +" + slots); + } else { + lore.add("§7格子: §e+" + slots); + lore.add("§7时间: §e" + days + "天"); + } + if (stock == 0) { + lore.add("§7库存: §a无限"); + } else if (stock == -1) { + lore.add("§7库存: §c已下架"); + } else { + lore.add("§7库存: §e" + stock); + } + lore.add(""); + if (points >= price && stock != -1) { + lore.add("§e点击购买"); + } else if (stock == -1) { + lore.add("§c已下架"); + } else { + lore.add("§c积分不足"); + } + inv.setItem(i, mkItem(Material.CHEST, + "§a" + name, + lore.toArray(new String[0]))); + } + inv.setItem(size - 1, + mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public void addItem(String name, + int price, String cmd2) { + // 追加到商品配置文件 + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.io.FileWriter fw = + new java.io.FileWriter(file, true); + fw.write(name + ":" + price + + ":" + cmd2 + "\n"); + fw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void removeItem(int index) { + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.util.List lines = + new java.util.ArrayList<>(); + java.io.BufferedReader br = + new java.io.BufferedReader( + new java.io.FileReader( + file)); + String line; + int i = 0; + while ((line = br.readLine()) != null) { + if (line.trim().isEmpty() + || line.startsWith("#")) { + lines.add(line); + continue; + } + if (i != index) { + lines.add(line); + } + i++; + } + br.close(); + java.io.PrintWriter pw = + new java.io.PrintWriter(file); + for (String l : lines) pw.println(l); + pw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void handleClick(Player p, int slot) { + int points = getPoints(p.getName()); + switch (slot) { + case 10: + if (points >= 100) { + plugin.getDb().deductPoints( + p.getName(), 100); + if (plugin.getEconomy() != null) { + plugin.getEconomy() + .depositPlayer(p, 1000); + } + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "100")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 12: + if (points >= 50) { + plugin.getDb().deductPoints( + p.getName(), 50); + activateCY(p.getName(), 9, 0); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "50")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 14: + if (points >= 20) { + plugin.getDb().deductPoints( + p.getName(), 20); + p.setLevel(p.getLevel() + 10); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "20")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 31: + if (cyInstance != null) { + p.openInventory( + createCYShopGUI(p)); + } + break; + } + } + + public void handleCYClick(Player p, int slot) { + if (slot < 0 || slot >= cyItems.size() + || slot >= 45) return; + Map item = cyItems.get(slot); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + if (stock == -1) { + p.sendMessage("§c该商品已下架"); + return; + } + int points = getPoints(p.getName()); + if (points < price) { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + return; + } + + plugin.getDb().deductPoints(p.getName(), price); + int actDays = lifetime ? 0 : days; + activateCY(p.getName(), slots, actDays); + + String tl = lifetime + ? "终身" : (days + "天"); + p.sendMessage("§a购买成功!获得 " + + tl + " +" + slots + "格 背包空间"); + } + + private void fillBg(Inventory inv) { + ItemStack gl = mkItem( + Material.GRAY_STAINED_GLASS_PANE, " "); + for (int i = 0; i < inv.getSize(); i++) + inv.setItem(i, gl); + } + + private ItemStack mkItem(Material mat, String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src/main/java/Sdf1_login/QuestStorage.java b/backup/src/main/java/Sdf1_login/QuestStorage.java new file mode 100644 index 0000000..45dbabf --- /dev/null +++ b/backup/src/main/java/Sdf1_login/QuestStorage.java @@ -0,0 +1,138 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class QuestStorage { + + private Connection conn; + + public QuestStorage(File dataFolder) { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + dataFolder, + "quest_data.db"); + conn = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getPath()); + initTable(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void initTable() + throws SQLException { + Statement stmt = + conn.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_conditions (" + + "player TEXT NOT NULL, " + + "condition_key TEXT NOT NULL, " + + "value TEXT DEFAULT '1', " + + "timestamp INTEGER, " + + "PRIMARY KEY " + + "(player, condition_key))"); + stmt.close(); + } + + /** + * 标记条件已完成(value = "1") + */ + public void setCondition( + String player, + String condition) { + setConditionRaw( + player, condition, "1"); + } + + /** + * 写入任意值(计数器等) + */ + public void setConditionRaw( + String player, + String condition, + String value) { + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO " + + "quest_conditions " + + "(player, " + + "condition_key, " + + "value, timestamp) " + + "VALUES " + + "(?, ?, ?, ?)"); + ps.setString(1, player); + ps.setString(2, condition); + ps.setString(3, value); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 查询条件值 + */ + public Object getCondition( + String player, + String condition) { + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value FROM " + + "quest_conditions " + + "WHERE player = ? " + + "AND " + + "condition_key = ?"); + ps.setString(1, player); + ps.setString(2, condition); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + String val = + rs.getString("value"); + rs.close(); + ps.close(); + // 尝试解析为数字 + try { + if (val.contains(".")) { + return Double.parseDouble( + val); + } + return Long.parseLong(val); + } catch (NumberFormatException e) { + return val; + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/backup/src/main/java/Sdf1_login/QuestTracker.java b/backup/src/main/java/Sdf1_login/QuestTracker.java new file mode 100644 index 0000000..228c1ac --- /dev/null +++ b/backup/src/main/java/Sdf1_login/QuestTracker.java @@ -0,0 +1,3252 @@ +package Sdf1_login; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +// 如果 Bonds 在 Sdf1_login 包下: +import Sdf1_login.BondManager; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class QuestTracker { + + private final Plugin plugin; + private final QuestStorage storage; + private final Map + playerBiome = new HashMap<>(); + private final Map> + questCache = new HashMap<>(); + private final List + checkInRecords = new ArrayList<>(); + private final Map + lastCheckInPos = new HashMap<>(); + + public void recordCheckIn( + String player, + String biomeKey, + String category, + String worldName, + double x, double y, double z) { + String record = player + + "|" + worldName + + "|" + biomeKey + + "|" + category + + "|" + String.format( + "%.1f", x) + + "|" + String.format( + "%.1f", y) + + "|" + String.format( + "%.1f", z) + + "|" + System.currentTimeMillis(); + checkInRecords.add(record); + plugin.getLogger().info( + "[QuestTracker] 打卡记录: " + + record); + } + + public List getCheckInRecords() { + return new ArrayList<>(checkInRecords); + } + + public List getPlayerRecords( + String player) { + List list = new ArrayList<>(); + for (String r : checkInRecords) { + if (r.startsWith( + player.toLowerCase() + + "|")) { + list.add(r); + } + } + return list; + } + + + public QuestTracker(Plugin plugin) { + this.plugin = plugin; + this.storage = new QuestStorage( + plugin.getDataFolder()); + + loadAllQuests(); + } + + public void debugPlayer(String player) { + plugin.getLogger().info( + "[QuestTracker] === " + + player + " 任务数据 ==="); + } + public void shutdown() { + if (storage != null) { + storage.close(); + } + } + + /** + * 玩家移动时记录当前群系 + */ + public void onPlayerMove( + String player, + String biomeName) { + playerBiome.put(player, biomeName); + } + /** + * 标记玩家已打卡该群系 + */ + // ========== 群系分类映射表 ========== + private static final Map + BIOME_CATEGORY = new HashMap<>(); + static { + // 海洋群系 + BIOME_CATEGORY.put("minecraft:ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:frozen_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_frozen_ocean", "海洋"); + // 平原群系 + BIOME_CATEGORY.put("minecraft:plains", "平原"); + BIOME_CATEGORY.put("minecraft:sunflower_plains", "平原"); + BIOME_CATEGORY.put("minecraft:meadow", "平原"); + // 森林群系 + BIOME_CATEGORY.put("minecraft:forest", "森林"); + BIOME_CATEGORY.put("minecraft:flower_forest", "森林"); + BIOME_CATEGORY.put("minecraft:birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:dark_forest", "森林"); + BIOME_CATEGORY.put("minecraft:taiga", "森林"); + BIOME_CATEGORY.put("minecraft:snowy_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_pine_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_spruce_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:cherry_grove", "森林"); + BIOME_CATEGORY.put("minecraft:jungle", "森林"); + BIOME_CATEGORY.put("minecraft:sparse_jungle", "森林"); + BIOME_CATEGORY.put("minecraft:bamboo_jungle", "森林"); + // 沙漠群系 + BIOME_CATEGORY.put("minecraft:desert", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna_plateau", "沙漠"); + BIOME_CATEGORY.put("minecraft:beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:snowy_beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:windswept_savanna", "沙漠"); + // 恶地群系 + BIOME_CATEGORY.put("minecraft:badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:wooded_badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:eroded_badlands", "恶地"); + // 雪原群系 + BIOME_CATEGORY.put("minecraft:snowy_plains", "雪原"); + BIOME_CATEGORY.put("minecraft:grove", "雪原"); + BIOME_CATEGORY.put("minecraft:ice_spikes", "雪原"); + BIOME_CATEGORY.put("minecraft:jagged_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_river", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:stony_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:snowy_slopes", "雪原"); + // 沙砾群系 + BIOME_CATEGORY.put("minecraft:windswept_hills", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_forest", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_gravelly_hills", "沙砾"); + // 沼泽群系 + BIOME_CATEGORY.put("minecraft:swamp", "沼泽"); + BIOME_CATEGORY.put("minecraft:mangrove_swamp", "沼泽"); + // 河流 + BIOME_CATEGORY.put("minecraft:river", "河流"); + // 蘑菇岛 + BIOME_CATEGORY.put("minecraft:mushroom_fields", "蘑菇岛"); + // 地下群系 + BIOME_CATEGORY.put("minecraft:dripstone_caves", "地下"); + BIOME_CATEGORY.put("minecraft:lush_caves", "地下"); + BIOME_CATEGORY.put("minecraft:deep_dark", "地下"); + // 下界群系 + BIOME_CATEGORY.put("minecraft:nether_wastes", "下界"); + BIOME_CATEGORY.put("minecraft:crimson_forest", "下界"); + BIOME_CATEGORY.put("minecraft:warped_forest", "下界"); + BIOME_CATEGORY.put("minecraft:soul_sand_valley", "下界"); + BIOME_CATEGORY.put("minecraft:basalt_deltas", "下界"); + // 岩岸/沙滩 + BIOME_CATEGORY.put("minecraft:stony_shore", + "石岸"); +// 海岸 + BIOME_CATEGORY.put("minecraft:mushroom_fields", + "蘑菇岛"); +// 苍白之园 + BIOME_CATEGORY.put( + "minecraft:pale_garden", "森林"); +// 末地小型岛屿 + BIOME_CATEGORY.put( + "minecraft:small_end_islands", "末地"); +// 主世界结构归类 + BIOME_CATEGORY.put("minecraft:village", "平原"); + BIOME_CATEGORY.put( + "minecraft:desert_pyramid", "沙漠"); + BIOME_CATEGORY.put( + "minecraft:jungle_pyramid", "森林"); + BIOME_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + + // 末地群系 + BIOME_CATEGORY.put("minecraft:the_end", "末地"); + BIOME_CATEGORY.put("minecraft:the_void", "末地"); + BIOME_CATEGORY.put("minecraft:end_highlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_midlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_barrens", "末地"); + + + } + + // 所有群系大类列表 + private static final String[] + ALL_CATEGORIES = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沙砾", "沼泽", + "河流", "石岸", "蘑菇岛", "地下", + "下界", "末地" + }; + + // ===== 结构映射 ===== + private static final Map + STRUCTURE_CATEGORY = + new HashMap<>(); + static { + // 主世界结构 + STRUCTURE_CATEGORY.put( + "minecraft:village", "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:desert_pyramid", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:jungle_pyramid", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + STRUCTURE_CATEGORY.put( + "minecraft:igloo", "雪原"); + STRUCTURE_CATEGORY.put( + "minecraft:mansion", "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:pillager_outpost", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:trail_ruins", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:trial_chambers", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:buried_treasure", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:fossil", "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "地下"); + // 下界结构 + STRUCTURE_CATEGORY.put( + "minecraft:nether_fortress", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:bastion_remnant", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:nether_fossil", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:basalt_pillars", + "下界"); + } + + // 所有群系分类的条件关键词映射 + private static final Map + CATEGORY_KEYWORDS = new HashMap<>(); + static { + CATEGORY_KEYWORDS.put("海洋", "海洋"); + CATEGORY_KEYWORDS.put("平原", "平原"); + CATEGORY_KEYWORDS.put("森林", "森林"); + CATEGORY_KEYWORDS.put("沙漠", "沙漠"); + CATEGORY_KEYWORDS.put("恶地", "恶地"); + CATEGORY_KEYWORDS.put("雪原", "雪原"); + CATEGORY_KEYWORDS.put("沙砾", "沙砾"); + CATEGORY_KEYWORDS.put("沼泽", "沼泽"); + CATEGORY_KEYWORDS.put("河流", "河流"); + CATEGORY_KEYWORDS.put("蘑菇岛", "蘑菇岛"); + CATEGORY_KEYWORDS.put("地下", "地下"); + CATEGORY_KEYWORDS.put("下界", "下界"); + CATEGORY_KEYWORDS.put("末地", "末地"); + // 条件文本中的别名映射 + CATEGORY_KEYWORDS.put("地狱", "下界"); + CATEGORY_KEYWORDS.put("下届", "下界"); + CATEGORY_KEYWORDS.put("主岛", "末地"); + CATEGORY_KEYWORDS.put("石岸", "石岸"); + CATEGORY_KEYWORDS.put("沙滩", "石岸"); + CATEGORY_KEYWORDS.put("海岸", "石岸"); + + } + + /** + * 用世界维度名分类(最可靠) + * 世界名含 the_nether → 下界 + * 世界名含 the_end → 末地 + * 其余 → 主世界 + */ + public String mapBiomeCategory( + String rawKey, String worldName) { + // 优先用世界名判断维度 + if (worldName != null) { + String w = + worldName.toLowerCase(); + if (w.contains("nether")) + return "下界"; + if (w.contains("end")) + return "末地"; + } + + // 用群系映射表 + if (rawKey != null) { + String lower = + rawKey.toLowerCase(); + // 带 minecraft: 前缀的直接查 + String cat = + BIOME_CATEGORY.get(lower); + if (cat != null) + return cat; + + // 不带前缀的尝试补全 + String prefixed = + "minecraft:" + lower; + cat = BIOME_CATEGORY.get(prefixed); + if (cat != null) + return cat; + + // 模糊匹配群系名 + for (Map.Entry entry + : BIOME_CATEGORY.entrySet()) { + if (entry.getKey() + .contains(lower) + || lower.contains( + entry.getKey() + .replace( + "minecraft:", + ""))) { + return entry.getValue(); + } + } + } + + return "主世界"; + } + + + +// ========== 解析为区块 ========== + + private List

    parseIntoSections( + List lines) { + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + return parseHashSections(lines); + } else if (hasDash) { + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + continue; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseHashSections(converted); + } + return new ArrayList<>(); + } + + private List
    parseHashSections( + List lines) { + List
    sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add(new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } else if (t.isEmpty()) { + // skip + } else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty() + && curTitle != null) { + curLines.add(cond); + } + } + } + if (curTitle != null) { + sections.add(new Section( + curTitle, curLines)); + } + return sections; + } + + private List
    buildFlatSections( + List lines, + String category) { + List conditions = + new ArrayList<>(); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + conditions.add(cond); + } + } + if (conditions.isEmpty()) + return new ArrayList<>(); + List
    list = + new ArrayList<>(); + list.add(new Section( + "第一阶段", conditions)); + return list; + } + + // ========== 文件加载入口 ========== + + private void loadAllQuests() { + loadCategory("新人任务"); + loadCategory("主线任务"); + loadCategory("支线任务"); + } + + private void loadCategory(String name) { + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 扫描中..."); + File folder = new File( + plugin.getDataFolder(), + name); + String path = "plugins/Sdf1_login/" + + name + "/"; + + if (!folder.exists()) { + folder.mkdirs(); + plugin.getLogger().warning( + "[QuestTracker] 已创建目录: " + + path); + questCache.put(name, + new ArrayList<>()); + return; + } + + File[] mdFiles = folder.listFiles( + (d, n) -> n.endsWith(".md")); + + if (mdFiles == null + || mdFiles.length == 0) { + plugin.getLogger().warning( + "[QuestTracker] " + path + + " 下无 .md 文件"); + questCache.put(name, + new ArrayList<>()); + return; + } + Arrays.sort(mdFiles); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 发现 " + mdFiles.length + + " 个文件,开始扫描..."); + + // 合并所有文件的阶段 + TreeMap merged = + new TreeMap<>(); + int fileCount = 0; + + for (int fi = 0; + fi < mdFiles.length; fi++) { + File file = mdFiles[fi]; + List lines = + readFileLines(file); + + if (lines.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 空文件,跳过"); + continue; + } + + List
    sections = + parseIntoSections(lines); + + if (sections.isEmpty()) { + sections = buildFlatSections( + lines, name); + } + + if (sections.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 无法识别,跳过"); + continue; + } + + // 每个文件独立处理阶段号 + int defaultNum = fi + 1; + buildSingleFileStages( + sections, name, + merged, defaultNum); + + fileCount++; + } + + if (merged.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + name + + ": 无有效任务数据"); + questCache.put(name, + new ArrayList<>()); + return; + } + + // 日志 + for (Map.Entry en + : merged.entrySet()) { + int num = en.getKey() >= 1000 + ? en.getKey() - 999 + : en.getKey(); + plugin.getLogger().info( + "[QuestTracker] " + name + + " 阶段" + num + + " \"" + + en.getValue().displayName + + "\" → 条件" + + en.getValue() + .conditions.size() + + "项, 详情" + + en.getValue() + .details.size() + + "项, 奖励" + + en.getValue() + .rewards.size() + + "项"); + } + + List list = + new ArrayList<>( + merged.values()); + questCache.put(name, list); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 解析完成 → " + + list.size() + " 个阶段" + + " (来自 " + fileCount + + " 个文件)"); + } + + private Integer extractStageNumber( + String title) { + if (title == null) return null; + String t = title.toLowerCase().trim(); + + // 含阶段/stage/关/chapter 关键词 + boolean isStage = + t.contains("阶段") + || t.contains("stage") + || t.contains("关卡") + || t.contains("章节") + || t.contains("chapter"); + + if (isStage) { + String digits = title + .replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + try { + return Integer.parseInt(digits); + } catch (NumberFormatException e) { + // fall through + } + } + return parseChineseNumber(title); + } + + // 无关键词但以数字开头(如 "1. 挖掘") + String trimmed = t + .replaceAll("^[.、\\s]+", "") + .trim(); + if (trimmed.length() > 0 + && Character.isDigit( + trimmed.charAt(0))) { + String num = trimmed + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) { + try { + return Integer.parseInt(num); + } catch (NumberFormatException e) { + // fall through + } + } + } + + return null; + } +// ========== 领取记录 ========== + + /** + * 标记已领取某阶段奖励 + */ + public void markClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return; + // 用阶段编号代替显示名,保证稳定 + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + storage.setCondition(player, key); + } + + public boolean hasClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return false; + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + return toBool( + storage.getCondition( + player, key)); + } + + private int parseChineseNumber( + String title) { + String[] cn = {"一", "二", "三", + "四", "五", "六", "七", + "八", "九", "十"}; + if (title.contains("十")) { + for (int j = 0; j < 9; j++) { + if (title.contains(cn[j])) { + return 10 + j + 1; + } + } + return 10; + } + for (int i = 0; i < 9; i++) { + if (title.contains(cn[i])) { + return i + 1; + } + } + return 0; + } + + // ========== md 解析核心 ========== + + /** + * 读取文件所有行 + */ + private List readFileLines( + File file) { + List lines = + new ArrayList<>(); + try { + BufferedReader br = + new BufferedReader( + new FileReader(file)); + String line; + while ((line = br.readLine()) + != null) { + lines.add(line); + } + br.close(); + } catch (Exception e) { + plugin.getLogger().warning( + "[QuestTracker] 读取 " + + file.getName() + + " 失败: " + + e.getMessage()); + } + return lines; + } + + /** + * 自动检测格式并解析单个文件 + */ + private List parseMdFile( + File file, String category) { + List lines = + readFileLines(file); + if (lines.isEmpty()) + return new ArrayList<>(); + + // 检测分隔符 + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) + hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + // # 分隔格式 + return parseSections( + lines, category); + } else if (hasDash) { + // ---- 分隔格式:转换为 # 后复用 + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + title = "separator"; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseSections( + converted, category); + } else { + // 无分隔符:整文件一个阶段 + return parseFlat(lines, category); + } + } + + /** + * # 分隔格式 → 区块 → 阶段 + */ + private List parseSections( + List lines, + String category) { + // 拆分为区块 + List
    sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + + // # 开头 → 新区块 + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add( + new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } + // 空行/分隔线 → 跳过 + else if (t.isEmpty() + || t.startsWith("----") + || t.startsWith("====") + || t.startsWith("***")) { + // skip + } + // 普通行 → 区块内容 + else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + curLines.add(cond); + } + } + } + // 保存最后一个区块 + if (curTitle != null) { + sections.add( + new Section( + curTitle, curLines)); + } + + // 直接转为 QuestFile 列表 + List result = + new ArrayList<>(); + int num = 1; + for (Section sec : sections) { + QuestFile qf = new QuestFile(); + qf.displayName = + extractDisplayName( + sec.title); + qf.fileName = category; + qf.conditions = + new ArrayList<>(sec.lines); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + result.add(qf); + num++; + } + return result; + } + + /** + * 无分隔符:整文件 = 一个阶段 + */ + private List parseFlat( + List lines, + String category) { + List list = + new ArrayList<>(); + QuestFile qf = makeDefaultStage( + category); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + qf.conditions.add(cond); + } + } + if (!qf.conditions.isEmpty()) { + list.add(qf); + } + return list; + } +// ========== Vault 经济奖励发放 ========== + + /** + * 发放阶段奖励 + * 含经济内容的走 Vault API或债券 + */ + public void claimRewards( + Player player, + QuestFile qf) { + if (qf == null + || qf.rewards == null + || qf.rewards.isEmpty()) { + player.sendMessage("§c无可领取的奖励"); + return; + } + + Main main = (Main) plugin; + // ★ 读取配置:economy = Vault,bonds = 债券(默认) + boolean useEconomy = "economy".equalsIgnoreCase( + main.getConfigMgr().rewardChannel); + + // Vault 经济引用(仅 useEconomy 时使用) + net.milkbowl.vault.economy.Economy econ = null; + if (useEconomy) { + econ = main.getEconomy(); + } + + int sentCount = 0; + + for (String reward : qf.rewards) { + String rt = reward.trim(); + if (rt.isEmpty()) continue; + + // 检查可选奖励标记 + if (hasOptionalMarker(rt)) { + if (!areAllConditionsMet(player.getName(), qf)) { + player.sendMessage( + "§7[可选奖励] 需完成所有可选条件: " + rt); + continue; + } + } + + // 按 + 分割子项 + String[] parts = rt.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 尝试识别经济金额 + double amt = extractEconomyAmount(part); + if (amt > 0) { + // ★ 根据配置分发 + if (useEconomy && econ != null) { + // —— Vault 经济 —— + double before = + econ.getBalance(player); + econ.depositPlayer(player, amt); + double after = + econ.getBalance(player); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(余额: §e" + + String.format("%.2f", before) + + " §7→ §a" + + String.format("%.2f", after) + + "§7)"); + if (plugin instanceof Main) { + ((Main) plugin).getCommission() + .payCommission( + player.getName(), amt); + } + } else { + // —— 债券(默认) —— + int bondAmt = + (int) Math.round(amt); + BondManager bonds = main.getBonds(); + if (bonds != null) { + int before = bonds.getBonds( + player.getName()); + bonds.addBonds( + player.getName(), + bondAmt, + "quest_reward", + qf.displayName, + "任务系统", + "任务奖励:" + qf.displayName); + int after = bonds.getBonds( + player.getName()); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(债券: §e" + + before + + " §7→ §a" + + after + "§7)"); + } else { + player.sendMessage( + "§c债券系统不可用"); + } + } + sentCount++; + continue; + } + + // 尝试发物品 + java.util.List items = + parseRewardItems(part); + if (!items.isEmpty()) { + for (ItemStack is : items) { + player.getInventory().addItem(is); + } + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + continue; + } + + // 非经济非物品 → 仅消息 + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + } + } + + plugin.getLogger().info( + "[QuestTracker] " + player.getName() + + " 预取了 \"" + qf.displayName + + "\" 共 " + sentCount + "项奖励"); + } + + /** + * 检查奖励文本是否含可选标记 + */ + private boolean hasOptionalMarker( + String text) { + return text.contains("(可选)") + || text.contains("(进阶)") + || text.contains("【可选】") + || text.contains("【进阶】"); + } + + /** + * 检查阶段所有条件(含可选)是否全部满足 + * 用于决定可选奖励是否发放 + */ + private boolean areAllConditionsMet( + String player, QuestFile qf) { + if (qf.conditions == null + || qf.conditions.isEmpty()) + return true; + for (String cond : qf.conditions) { + if (!isConditionMet(player, cond)) + return false; + } + return true; + } + + /** + * 从奖励文本中提取经济金额 + * 支持: "500游戏币" "500" "金币500" + * "$500" "1000-5000"(随机) "1000~5000" + */ + private double extractEconomyAmount( + String text) { + String t = text.replaceAll(" ", ""); + + // 必须包含经济关键词才算经济奖励 + boolean hasEcon = + t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额") + || t.contains("发钱") + || t.contains("债券"); // ★ 新增 + + + if (!hasEcon) return 0; + + // 范围: "1000-5000" 或 "1000~5000" + Matcher rm = Pattern + .compile("(\\d+)[-~](\\d+)") + .matcher(t); + if (rm.find()) { + double min = Double.parseDouble( + rm.group(1)); + double max = Double.parseDouble( + rm.group(2)); + return min + Math.random() + * (max - min); + } + + // 纯数字(含 $ 前缀) + Matcher nm = Pattern + .compile("\\$?(\\d+(\\.\\d+)?)") + .matcher(t); + if (nm.find()) { + return Double.parseDouble( + nm.group(1)); + } + + return 0; + } + /** + * 解析奖励物品文本 + * "皮革套装*1" → 4件皮革装备 + * "木剑*1" → 1把木剑 + * "钻石*64" → 64颗钻石 + */ + private List parseRewardItems( + String text) { + List result = + new ArrayList<>(); + + // 去掉条件前缀 + // 如 "完成注册登录:皮革套装*1" + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + + // 按 + 分割多个物品 + String[] parts = + text.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 跳过经济文本 + if (extractEconomyAmount(part) > 0) + continue; + + int amount = 1; + String itemName = part; + + // 解析 *N 数量 + if (part.contains("*")) { + String[] sp = + part.split("\\*"); + itemName = sp[0].trim(); + try { + amount = Integer.parseInt( + sp[1].trim()); + } catch (Exception e) { + amount = 1; + } + } + + List mats = + matchMaterials(itemName); + for (Material m : mats) { + int give = m.name() + .contains("HELMET") + || m.name() + .contains("CHESTPLATE") + || m.name() + .contains("LEGGINGS") + || m.name() + .contains("BOOTS") + ? 1 : amount; + result.add( + new ItemStack(m, give)); + } + } + return result; + } + + /** + * 中文物品名 → Material 列表 + * 套装返回多个部件 + */ + private List matchMaterials( + String name) { + List list = + new ArrayList<>(); + String n = name.toLowerCase().trim(); + + // 套装 + if (n.contains("皮革套装") + || n.contains("皮套装")) { + list.add(Material.LEATHER_HELMET); + list.add(Material.LEATHER_CHESTPLATE); + list.add(Material.LEATHER_LEGGINGS); + list.add(Material.LEATHER_BOOTS); + return list; + } + if (n.contains("铁套装") + || n.contains("铁甲")) { + list.add(Material.IRON_HELMET); + list.add(Material.IRON_CHESTPLATE); + list.add(Material.IRON_LEGGINGS); + list.add(Material.IRON_BOOTS); + return list; + } + if (n.contains("钻石套装") + || n.contains("钻甲")) { + list.add(Material.DIAMOND_HELMET); + list.add(Material.DIAMOND_CHESTPLATE); + list.add(Material.DIAMOND_LEGGINGS); + list.add(Material.DIAMOND_BOOTS); + return list; + } + // 木制工具套装 + if (n.contains("木制工具") + || n.contains("木工具")) { + list.add(Material.WOODEN_SWORD); + list.add(Material.WOODEN_PICKAXE); + list.add(Material.WOODEN_AXE); + list.add(Material.WOODEN_SHOVEL); + return list; + } + if (n.contains("石制工具") + || n.contains("石工具")) { + list.add(Material.STONE_SWORD); + list.add(Material.STONE_PICKAXE); + list.add(Material.STONE_AXE); + list.add(Material.STONE_SHOVEL); + return list; + } + if (n.contains("铁工具")) { + list.add(Material.IRON_SWORD); + list.add(Material.IRON_PICKAXE); + list.add(Material.IRON_AXE); + list.add(Material.IRON_SHOVEL); + return list; + } + + // 单个物品 + if (n.contains("木剑")) + list.add(Material.WOODEN_SWORD); + else if (n.contains("木镐")) + list.add(Material.WOODEN_PICKAXE); + else if (n.contains("铁剑")) + list.add(Material.IRON_SWORD); + else if (n.contains("铁镐")) + list.add(Material.IRON_PICKAXE); + else if (n.contains("钻石剑")) + list.add(Material.DIAMOND_SWORD); + else if (n.contains("钻石镐")) + list.add(Material.DIAMOND_PICKAXE); + else if (n.contains("钻石")) + list.add(Material.DIAMOND); + else if (n.contains("铁锭")) + list.add(Material.IRON_INGOT); + else if (n.contains("金锭") + || n.contains("金块")) + list.add(Material.GOLD_INGOT); + else if (n.contains("煤炭")) + list.add(Material.COAL); + else if (n.contains("木棍")) + list.add(Material.STICK); + else if (n.contains("弓")) + list.add(Material.BOW); + else if (n.contains("箭")) + list.add(Material.ARROW); + else if (n.contains("床")) + list.add(Material.RED_BED); + else { + // 尝试直接匹配 Material 名 + try { + list.add(Material.valueOf( + n.toUpperCase() + .replaceAll("[^A-Z_]", + ""))); + } catch (Exception e) { + // 未知物品 + } + } + return list; + } + + private QuestFile getOrCreateStage( + TreeMap map, + int key, + String category) { + if (!map.containsKey(key)) { + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + key; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + } + return map.get(key); + } + + /** + * 单个文件的区块 → 阶段 + * defaultStageNum: 文件无阶段标题时的默认编号 + */ + private void buildSingleFileStages( + List
    sections, + String category, + TreeMap stageMap, + int defaultStageNum) { + Integer curKey = null; + + for (Section sec : sections) { + Integer stageNum = + extractStageNumber(sec.title); + + if (stageNum != null) { + curKey = stageNum; + } else if (curKey == null) { + curKey = defaultStageNum; + } + + QuestFile qf = getOrCreateStage( + stageMap, curKey, category); + + String displayName = + extractDisplayName(sec.title); + + // 设置显示名 + if (!displayName.isEmpty() + && qf.displayName + .startsWith("阶段 ")) { + qf.displayName = displayName; + } + + // 路由 + if (stageNum != null + && sec.lines.isEmpty()) { + if (!displayName.isEmpty()) { + qf.displayName = displayName; + } + } + else if (isRewardTitle(sec.title)) { + // 奖励 → 只进 rewards + qf.rewards.addAll(sec.lines); + } + else if (isDetailsTitle(sec.title)) { + // 详情 → 进 details + conditions + qf.details.addAll(sec.lines); + for (String line : sec.lines) { + if (!qf.conditions + .contains(line)) { + qf.conditions.add(line); + } + } + } + else if (isConditionsTitle( + sec.title)) { + // 条件 → 只进 conditions + qf.conditions.addAll(sec.lines); + } + else { + // 其他 → 条件 + qf.conditions.addAll(sec.lines); + } + } + } + + /** + * "主线任务-斗志昂扬" → "斗志昂扬" + * "支线-钓鱼" → "钓鱼" + * "阶段 1" → "阶段 1" + * "第一阶段" → "第一阶段" + */ + private String extractDisplayName( + String title) { + if (title == null) return ""; + // 按 - 或 — 或 – 分割 + String[] parts = title.split( + "\\s*[-—–]\\s*", 2); + if (parts.length >= 2) { + String right = parts[1].trim(); + if (!right.isEmpty()) { + return right; + } + } + return title; + } + private DatabaseManager getDb() { + return ((Main) plugin).getDb(); + } + + private boolean isDetailsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("任务详情") + || t.contains("详情") + || t.contains("任务说明") + || t.contains("任务目标") + || t.contains("description") + || t.contains("任务要求"); + } + + private boolean isConditionsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("解锁条件") + || t.contains("条件") + || t.contains("前置条件") + || t.contains("前置要求") + || t.contains("解锁") + || t.contains("condition"); + } + + + private void ensureStage( + TreeMap map, + String category, + QuestFile current, + int counter) { + if (current != null) return; + // 用大数字确保排在有编号阶段后面 + int key = 1000 + counter; + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + (counter + 1); + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + // 更新调用方的引用(通过返回值) + // 这里用 map 操作,current 无法直接更新 + // 所以改用返回值方式 + } + /** + * 自动将条件中的奖励内容移到 rewards + */ + private void separateRewards(QuestFile qf) { + List remaining = + new ArrayList<>(); + for (String line : qf.conditions) { + if (isRewardContent(line)) { + if (!qf.rewards.contains(line)) { + qf.rewards.add(line); + } + } else { + remaining.add(line); + } + } + qf.conditions = remaining; + } + private boolean checkStageDependency( + String player, + String condition) { + Matcher m = Pattern.compile( + "第([一二三四五六七八九十\\d]+)" + + "阶段") + .matcher(condition); + + while (m.find()) { + String numStr = m.group(1); + int stageNum = + parseChineseNumber(numStr); + if (stageNum == 0) { + try { + stageNum = + Integer.parseInt(numStr); + } catch (Exception e) { + return false; + } + } + + boolean stageDone = false; + for (List quests + : questCache.values()) { + for (QuestFile qf : quests) { + Integer qfNum = + extractStageNumber( + qf.displayName); + if (qfNum != null + && qfNum == stageNum) { + if (isStageComplete(player, + qf.conditions)) { + stageDone = true; + } + break; + } + } + if (stageDone) break; + } + + if (!stageDone) return false; + } + return true; + } + + /** + * 判断一行内容是否为奖励 + */ + private boolean isRewardContent( + String line) { + String l = line.toLowerCase(); + // 含货币关键词 + if (l.contains("游戏币") + || l.contains("金币") + || l.contains("积分") + || l.contains("钻石") + || l.contains("发钱") + || l.contains("发放") + || l.contains("经验") + || l.contains("等级") + || l.contains("经济") + || l.contains("余额") + || l.contains("块钱")) + return true; + // 纯数字+单位(如 "500游戏币") + if (l.matches( + ".*\\d+\\s*(游戏币|金币|元|积分|" + + "钻石|块钱|经验|级|经济|余额).*")) + return true; + // $前缀(如 "$500") + if (l.matches(".*\\$\\d+.*")) + return true; + // 范围格式(如 "1000-5000" 或 "1000~5000") + if (l.matches(".*\\d+[-~]\\d+.*") + && !l.contains("阶段") + && !l.contains("stage")) + return true; + return false; + } + + // ========== 标题分类 ========== + + private boolean isStageTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("阶段") + || t.contains("stage") + || t.contains("第一") + || t.contains("第二") + || t.contains("第三") + || t.contains("第四") + || t.contains("第五"); + } + + private boolean isRewardTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("奖励") + || t.contains("奖品") + || t.contains("报酬") + || t.contains("reward") + || t.contains("奖赏") + || t.contains("礼包内容") + || t.contains("礼包") + || t.contains("物品奖励"); + } + + + private QuestFile makeDefaultStage( + String category) { + QuestFile qf = new QuestFile(); + qf.displayName = "第一阶段"; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + return qf; + } + + private static class Section { + final String title; + final List lines; + Section(String title, + List lines) { + this.title = title; + this.lines = lines; + } + } + + // ========== 公开查询 ========== + + public List getQuests( + String category) { + List list = + questCache.get(category); + return list != null ? list + : new ArrayList<>(); + } + + public int getCompletedStageCount( + String player, + List quests) { + int count = 0; + if (quests == null) return 0; + for (QuestFile qf : quests) { + if (isStageComplete(player, + qf.conditions)) { + count++; + } + } + return count; + } + + public QuestFile getCurrentStage( + String player, + List quests) { + if (quests == null) return null; + for (QuestFile qf : quests) { + if (!isStageComplete( + player, qf.conditions)) { + return qf; + } + } + return null; + } + + public int getStageNumber( + List quests, + QuestFile qf) { + if (quests == null || qf == null) + return 0; + return quests.indexOf(qf) + 1; + } + + public int getStageNumber( + QuestFile qf) { + if (qf == null) return 0; + List quests = + getQuests(qf.fileName); + return getStageNumber(quests, qf); + } + + public boolean isStageCompleted( + String player, + QuestFile qf) { + if (qf == null) return false; + return isStageComplete( + player, qf.conditions); + } + + public List getUnmetConditions( + String player, + List conditions) { + List unmet = + new ArrayList<>(); + if (conditions == null) return unmet; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) { + unmet.add( + getProgress( + player, cond)); + } + } + return unmet; + } + public String getProgress( + String player, String rawCond) { + if (player == null || rawCond == null) + return "§7未知"; + String key = stripOptional(rawCond); + + if (key.contains("打卡") + && key.contains("群系")) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return "§7打卡 " + biome + + "群系: §e" + + catCount + "/1"; + } + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + + if (key.contains("完成") + && key.contains("阶段")) { + boolean done = checkStageDependency( + player, key); + return done + ? "§a✓ " + key + : "§c✗ " + key; + } + + if ("完成注册登录".equals(key)) { + boolean done = getDb() != null + && getDb().userExists( + player); + return done + ? "§a✓ 完成注册登录" + : "§c✗ 完成注册登录"; + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + boolean done = val != null + && !val.toString().isEmpty(); + return done + ? "§a✓ 绑定Email" + : "§c✗ 绑定Email"; + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + needed + "小时"; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + int cur = getCounter( + player, "CHAT"); + return "§7聊天: §e" + + cur + "/1"; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return "§7方块操作: §e" + + total + "/" + needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return "§7连续登录: §e" + + streak + "/" + needed + "天"; + } + + if (key.startsWith("MINE:")) { + String[] parts = key.split(":"); + if (parts.length >= 3) { + int needed = + parseInt(parts[2]); + int cur = getCounter( + player, + "MINE:" + parts[1]); + return "§7挖 " + parts[1] + + ": §e" + cur + + "/" + needed; + } + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "VILLAGER_TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + + } + + // ===== 击杀泛解析(支持中文列表) ===== +// 匹配 "击杀10只骷髅、僵尸、苦力怕" 等 + if (key.contains("击杀") && key.contains("只")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + + String mobPart = key; + java.util.regex.Matcher killM = + java.util.regex.Pattern + .compile("击杀\\d+只(.+)") + .matcher(key); + if (killM.find()) { + mobPart = killM.group(1).trim(); + } + + String[] mobNames = mobPart + .split("[、,,,]+"); + int totalKills = 0; + StringBuilder names = new StringBuilder(); + for (String name : mobNames) { + String trimmed = name.trim(); + if (trimmed.isEmpty()) continue; + String entityType = + chineseMobToEntity(trimmed); + if (!entityType.isEmpty()) { + totalKills += getCounter( + player, + "KILL:" + entityType); + if (names.length() > 0) + names.append("/"); + names.append(trimmed); + } + } + return "§7击杀" + names + ": §e" + + totalKills + "/" + needed; + } + +// 单一KILL:xxx:N 格式(原有逻辑保留) + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + int needed = parseInt(p[2]); + int cur = getCounter(player, + "KILL:" + p[1]); + return "§7击杀" + p[1] + ": §e" + + cur + "/" + needed; + } + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "KILL_PLAYER"); + return "§7击杀玩家: §e" + + cur + "/" + needed; + } + + if (key.startsWith("CRAFT")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "CRAFT"); + return "§7合成: §e" + + cur + "/" + needed; + } + + if (key.startsWith("FISH")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "FISH"); + return "§7钓鱼: §e" + + cur + "/" + needed; + } + + if (key.startsWith("PLAYTIME")) { + String[] parts = key.split(":"); + long needed = parts.length >= 2 + ? Long.parseLong(parts[1]) + : 0; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + formatMs(needed); + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7上报村庄: §e" + + cur + "/" + needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7探索村庄: §e" + + cur + "/1"; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return "§7近7天登录: §e" + + streak + "/" + + needed + "天"; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7新手任务: §e" + + done + "/" + + quests.size(); + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7主线任务: §e" + + done + "/" + + quests.size(); + } + // ===== 泛解析:击杀类进度 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < mobTypes.size(); i++) { + total += getCounter(player, + "KILL:" + mobTypes.get(i)); + if (i > 0) names.append("/"); + names.append(reverseMobName( + mobTypes.get(i))); + } + return "§7击杀" + names + ": §e" + + total + "/" + needed; + } + } + + // ===== 泛解析:村民交易进度 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + // ===== 泛解析:采集/挖矿进度 ===== + if (key.contains("采集") + || key.contains("挖")) { + int needed = + parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < matTypes.size(); i++) { + total += getCounter( + player, + "MINE:" + matTypes + .get(i)); + if (i > 0) + names.append("/"); + names.append( + translateMaterialName( + matTypes + .get(i))); + } + return "§7采集" + names + + ": §e" + total + + "/" + needed; + } + } + + // ===== 泛解析:合成进度 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "CRAFT"); + return "§7合成: §e" + cur + "/" + needed; + } + + // ===== 泛解析:钓鱼进度 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "FISH"); + return "§7钓鱼: §e" + cur + "/" + needed; + } + + return "§7" + key; + } + private String translateMaterialName( + String name) { + switch (name) { + case "COAL_ORE": + return "煤矿石"; + case "DEEPSLATE_COAL_ORE": + return "深层煤矿石"; + case "IRON_ORE": + return "铁矿石"; + case "DEEPSLATE_IRON_ORE": + return "深层铁矿石"; + case "COPPER_ORE": + return "铜矿石"; + case "DEEPSLATE_COPPER_ORE": + return "深层铜矿石"; + case "GOLD_ORE": + return "金矿石"; + case "DEEPSLATE_GOLD_ORE": + return "深层金矿石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "DEEPSLATE_DIAMOND_ORE": + return "深层钻石矿石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "DEEPSLATE_EMERALD_ORE": + return "深层绿宝石矿石"; + case "LAPIS_ORE": + return "青金石矿石"; + case "DEEPSLATE_LAPIS_ORE": + return "深层青金石矿石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "DEEPSLATE_REDSTONE_ORE": + return "深层红石矿石"; + case "NETHER_GOLD_ORE": + return "下界金矿石"; + case "NETHER_QUARTZ_ORE": + return "下界石英矿石"; + case "ANCIENT_DEBRIS": + return "远古残骸"; + case "COAL": + return "煤炭"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "RAW_COPPER": + return "粗铜"; + case "DIAMOND": + return "钻石"; + case "EMERALD": + return "绿宝石"; + case "LAPIS_LAZULI": + return "青金石"; + case "REDSTONE": + return "红石"; + case "QUARTZ": + return "石英"; + default: + return name; + } + } + + /** + * 中文物种名 → Bukkit EntityType 名 + * 支持击杀泛解析的模糊匹配 + */ + private String chineseMobToEntity(String cn) { + String n = cn.toLowerCase().trim(); + + // 直接别名表 + Map mobMap = new HashMap<>(); + mobMap.put("骷髅", "SKELETON"); + mobMap.put("僵尸", "ZOMBIE"); + mobMap.put("苦力怕", "CREEPER"); + mobMap.put("蜘蛛", "SPIDER"); + mobMap.put("洞穴蜘蛛", "CAVE_SPIDER"); + mobMap.put("小白", "SKELETON"); + mobMap.put("末影人", "ENDERMAN"); + mobMap.put("enderman", "ENDERMAN"); + mobMap.put("猪灵", "PIGLIN"); + mobMap.put("猪灵蛮兵", "PIGLIN_BRUTE"); + mobMap.put("恶魂", "GHAST"); + mobMap.put("凋灵骷髅", "WITHER_SKELETON"); + mobMap.put("烈焰人", "BLAZE"); + mobMap.put("史莱姆", "SLIME"); + mobMap.put("岩浆怪", "MAGMA_CUBE"); + mobMap.put("女巫", "WITCH"); + mobMap.put("蠹虫", "SILVERFISH"); + mobMap.put("潜影贝", "SHULKER"); + mobMap.put("守卫者", "GUARDIAN"); + mobMap.put("远古守卫者", "ELDER_GUARDIAN"); + mobMap.put("溺尸", "DROWNED"); + mobMap.put("僵尸村民", "ZOMBIE_VILLAGER"); + mobMap.put("尸壳", "HUSK"); + mobMap.put("流浪者", "STRAY"); + mobMap.put("幻翼", "PHANTOM"); + mobMap.put("掠夺者", "PILLAGER"); + mobMap.put("卫道士", "VINDICATOR"); + mobMap.put("唤魔者", "EVOKER"); + mobMap.put("恼鬼", "VEX"); + mobMap.put("劫掠兽", "RAVAGER"); + mobMap.put("蜘蛛骑士", "SPIDER_JOCKEY"); + mobMap.put("凋灵", "WITHER"); + mobMap.put("末影龙", "ENDER_DRAGON"); + + // 精确匹配 + if (mobMap.containsKey(n)) { + return mobMap.get(n); + } + + // 模糊匹配(包含关系) + for (Map.Entry en + : mobMap.entrySet()) { + if (n.contains(en.getKey()) + || en.getKey().contains(n)) { + return en.getValue(); + } + } + + // 尝试直接用英文名 + try { + return org.bukkit.entity.EntityType + .valueOf(n.toUpperCase() + .replace(" ", "_")) + .name(); + } catch (Exception e) { + return ""; + } + } + + public List checkInBiome( + String player, + String rawBiomeKey, + String worldName, + double x, double y, double z) { + List matched = + new ArrayList<>(); + + // 32格去重 + String posKey = + player + "_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + String lastPos = + lastCheckInPos.get(posKey); + if (curPos.equals(lastPos)) { + plugin.getLogger().info( + "[QuestTracker] 打卡忽略: " + + player + + " 32格内重复"); + return matched; + } + lastCheckInPos.put(posKey, curPos); + + String category = + mapBiomeCategory( + rawBiomeKey, worldName); + // 结构检测(覆盖纯生物群系分类) + // 地下检测:Y低于表面则归类为地下 +// 覆盖废弃矿井/试炼密室/要塞/远古城市等 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + + + // 记录总打卡 + int allCur = getCounter( + player, "biome_ALL"); + setCounter(player, "biome_ALL", + allCur + 1); + + // 记录分类打卡 + if (!category.isEmpty() + && !"主世界" + .equals(category)) { + String catKey = + "biomecat_" + category; + int catCur = getCounter( + player, catKey); + setCounter(player, catKey, + catCur + 1); + } + + // 记录具体群系 + String bioKey = + "biome_" + rawBiomeKey; + int bioCur = getCounter( + player, bioKey); + setCounter(player, bioKey, + bioCur + 1); + + // 检查所有任务条件是否匹配 + List categories = + Arrays.asList( + "新人任务", + "主线任务", + "支线任务"); + + for (String cat : categories) { + List quests = + getQuests(cat); + for (QuestFile qf : quests) { + if (isStageCompleted( + player, qf)) + continue; + for (String cond + : qf.conditions) { + if (isOptional(cond)) + continue; + String condKey = + stripOptional(cond); + if (!condKey + .contains("打卡") + || !condKey + .contains("群系")) + continue; + String target = + extractBiome(condKey); + boolean hit = false; + if ("ALL".equals(target) + || "风景点" + .equals(target)) { + hit = true; + } else if (!target.isEmpty() + && target.equals( + category)) { + hit = true; + } + if (hit && !matched + .contains(cond)) { + matched.add(cond); + } + } + } + } + + return matched; + } + + /* + * 打卡入口:报告坐标 → 32格去重 → 匹配条件 → 记录 + */ + public String checkInAtPosition( + String player, String worldName, + double x, double y, double z) { + + List history = + loadCheckInPositions(player); + for (double[] pos : history) { + double dist = Math.sqrt( + Math.pow(x - pos[0], 2) + + Math.pow(y - pos[1], 2) + + Math.pow(z - pos[2], 2)); + if (dist <= 32) { + return "§c距离上次打卡点太近(" + + String.format( + "%.0f", dist) + + "格)"; + } + } + + // ===== 获取群系和分类 ===== + String biome = "minecraft:plains"; + String category = "平原"; + try { + org.bukkit.World w = + Bukkit.getWorld(worldName); + if (w != null) { + org.bukkit.Location loc = + new org.bukkit.Location( + w, + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + biome = loc.getBlock() + .getBiome().getKey() + .toString(); + category = mapBiomeCategory( + biome, worldName); + + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + + if ("下界".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.NETHER) { + category = "下界"; + } else if ("末地".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.THE_END) { + category = "末地"; + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 群系读取失败: " + + ex.getMessage()); + } + + plugin.getLogger().info( + "[QuestTracker] 打卡: " + + player + + " world=" + worldName + + " biome=" + biome + + " cat=" + category + + " pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + + saveCheckInPosition( + player, x, y, z); + recordCheckIn( + player, biome, category, + worldName, x, y, z); + List matched = + checkInBiome( + player, biome, + worldName, x, y, z); + + StringBuilder sb = new StringBuilder(); + sb.append("§a打卡成功!群系: §e") + .append(category); + if (!matched.isEmpty()) { + sb.append("\n§a完成任务条件:"); + for (String m : matched) { + String qn = + findQuestName(m); + sb.append("\n§7• "); + if (qn != null) { + sb.append("§e[") + .append(qn) + .append("§7] §f"); + } + sb.append(m); + } + } + return sb.toString(); + } + + + /** + * 根据条件文本查找所属任务名 + */ + private String findQuestName( + String condition) { + for (String cat : Arrays.asList( + "新人任务", + "主线任务", + "支线任务")) { + for (QuestFile qf : + getQuests(cat)) { + if (qf.conditions.contains( + condition)) { + return qf.displayName; + } + } + } + return null; + } + + + /** + * 加载玩家所有历史打卡坐标 + * 格式: x1,y1,z1;x2,y2,z2;... + */ + private List loadCheckInPositions( + String player) { + List list = new ArrayList<>(); + Object val = storage.getCondition( + player, "checkin_all_pos"); + if (val == null) return list; + String data = val.toString(); + if (data.isEmpty()) return list; + String[] records = data.split(";"); + for (String r : records) { + String[] parts = r.split(","); + if (parts.length >= 3) { + try { + list.add(new double[]{ + Double.parseDouble(parts[0]), + Double.parseDouble(parts[1]), + Double.parseDouble(parts[2])}); + } catch (NumberFormatException e) { + // 跳过损坏数据 + } + } + } + return list; + } + + /** + * 保存打卡坐标(追加到已有记录) + */ + private void saveCheckInPosition( + String player, double x, + double y, double z) { + List list = + loadCheckInPositions(player); + list.add(new double[]{x, y, z}); + StringBuilder sb = new StringBuilder(); + for (double[] pos : list) { + if (sb.length() > 0) sb.append(";"); + sb.append(pos[0]).append(",") + .append(pos[1]).append(",") + .append(pos[2]); + } + storage.setConditionRaw( + player, + "checkin_all_pos", + sb.toString()); + } + // ========== 中文生物名 → EntityType 映射 ========== + private static final Map + MOB_NAME_MAP = new HashMap<>(); + static { + MOB_NAME_MAP.put("洞穴蜘蛛", "CAVE_SPIDER"); + MOB_NAME_MAP.put("骷髅", "SKELETON"); + MOB_NAME_MAP.put("僵尸", "ZOMBIE"); + MOB_NAME_MAP.put("苦力怕", "CREEPER"); + MOB_NAME_MAP.put("蜘蛛", "SPIDER"); + MOB_NAME_MAP.put("末影人", "ENDERMAN"); + MOB_NAME_MAP.put("女巫", "WITCH"); + MOB_NAME_MAP.put("烈焰人", "BLAZE"); + MOB_NAME_MAP.put("僵尸猪灵", + "ZOMBIFIED_PIGLIN"); + MOB_NAME_MAP.put("猪灵", "PIGLIN"); + MOB_NAME_MAP.put("恶魂", "GHAST"); + MOB_NAME_MAP.put("凋灵骷髅", + "WITHER_SKELETON"); + MOB_NAME_MAP.put("史莱姆", "SLIME"); + MOB_NAME_MAP.put("岩浆怪", "MAGMA_CUBE"); + MOB_NAME_MAP.put("溺尸", "DROWNED"); + MOB_NAME_MAP.put("尸壳", "HUSK"); + MOB_NAME_MAP.put("流髑", "STRAY"); + MOB_NAME_MAP.put("幻翼", "PHANTOM"); + MOB_NAME_MAP.put("守卫者", "GUARDIAN"); + MOB_NAME_MAP.put("远古守卫者", + "ELDER_GUARDIAN"); + MOB_NAME_MAP.put("掠夺者", "PILLAGER"); + MOB_NAME_MAP.put("卫道士", "VINDICATOR"); + MOB_NAME_MAP.put("唤魔者", "EVOKER"); + MOB_NAME_MAP.put("恼鬼", "VEX"); + MOB_NAME_MAP.put("劫掠兽", "RAVAGER"); + MOB_NAME_MAP.put("潜影贝", "SHULKER"); + MOB_NAME_MAP.put("末影螨", "ENDERMITE"); + MOB_NAME_MAP.put("凋灵", "WITHER"); + MOB_NAME_MAP.put("末影龙", "ENDER_DRAGON"); + MOB_NAME_MAP.put("监守者", "WARDEN"); + MOB_NAME_MAP.put("嗅探兽", "SNIFFER"); + MOB_NAME_MAP.put("僵尸村民", + "ZOMBIE_VILLAGER"); + } + + /** + * 从条件文本中提取所有匹配的生物类型 + * 按名称长度降序匹配,避免短名误匹配 + */ + private List extractMobTypes( + String text) { + List types = new ArrayList<>(); + List> sorted = + new ArrayList<>( + MOB_NAME_MAP.entrySet()); + sorted.sort((a, b) -> + b.getKey().length() + - a.getKey().length()); + String remaining = text; + for (Map.Entry entry + : sorted) { + if (remaining.contains( + entry.getKey())) { + if (!types.contains( + entry.getValue())) { + types.add(entry.getValue()); + } + remaining = remaining.replace( + entry.getKey(), ""); + } + } + return types; + } + + /** + * EntityType → 中文名 + */ + private String reverseMobName( + String entityType) { + for (Map.Entry entry + : MOB_NAME_MAP.entrySet()) { + if (entry.getValue() + .equals(entityType)) { + return entry.getKey(); + } + } + return entityType; + } + + /** + * 从条件文本中提取矿物名 + * "采集任意矿物(煤炭除外)1组" + * → [COAL_ORE, IRON_ORE, GOLD_ORE, ...] + */ + private List extractMaterialTypes( + String text) { + List types = new ArrayList<>(); + // 排除列表 + List excludes = + new ArrayList<>(); + if (text.contains("煤炭除外")) { + excludes.add("COAL"); + excludes.add("COAL_ORE"); + excludes.add("DEEPSLATE_COAL_ORE"); + } + // 通配符:"矿物" "矿石" + if (text.contains("矿物") + || text.contains("矿石")) { + String[] ores = { + "COAL_ORE", + "DEEPSLATE_COAL_ORE", + "IRON_ORE", + "DEEPSLATE_IRON_ORE", + "COPPER_ORE", + "DEEPSLATE_COPPER_ORE", + "GOLD_ORE", + "DEEPSLATE_GOLD_ORE", + "DIAMOND_ORE", + "DEEPSLATE_DIAMOND_ORE", + "EMERALD_ORE", + "DEEPSLATE_EMERALD_ORE", + "LAPIS_ORE", + "DEEPSLATE_LAPIS_ORE", + "REDSTONE_ORE", + "DEEPSLATE_REDSTONE_ORE", + "NETHER_GOLD_ORE", + "NETHER_QUARTZ_ORE", + "ANCIENT_DEBRIS" + }; + for (String ore : ores) { + if (!excludes.contains(ore)) { + types.add(ore); + } + } + return types; + } + // 单个矿物名 + if (text.contains("煤炭")) + types.add("COAL"); + if (text.contains("铁矿") + || text.contains("铁锭")) + types.add("RAW_IRON"); + if (text.contains("金矿") + || text.contains("金锭")) + types.add("RAW_GOLD"); + if (text.contains("钻石")) + types.add("DIAMOND"); + if (text.contains("绿宝石")) + types.add("EMERALD"); + if (text.contains("青金石")) + types.add("LAPIS_LAZULI"); + if (text.contains("红石")) + types.add("REDSTONE"); + if (text.contains("铜")) + types.add("RAW_COPPER"); + if (text.contains("下界石英") + || text.contains("石英")) + types.add("QUARTZ"); + return types; + } + + + /** + * 从条件文本中提取目标群系分类 + * "打卡上报地狱任意群系" → "下界" + * "打卡任意海洋群系" → "海洋" + * "打卡任意群系" → "ALL" + * "打卡任意风景点" → "" + */ + private String extractBiome(String text) { + if (text == null) return ""; + // 逐个匹配分类关键词 + for (Map.Entry en + : CATEGORY_KEYWORDS.entrySet()) { + if (text.contains(en.getKey())) { + return en.getValue(); + } + } + if (text.contains("风景点") + || text.contains("风景")) { + return "ALL"; + } + + // 有"群系"但无具体分类 → ALL + if (text.contains("群系")) { + return "ALL"; + } + return ""; + } + + + // ========== 标记条件 ========== + + public void markConditionComplete( + String player, + String conditionName) { + if (player == null + || conditionName == null) + return; + storage.setCondition( + player, conditionName); + } + + // ========== 事件回调 ========== + + public void onBlockMined( + String player, String material) { + String key = "MINE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + // 总计数 + setCounter(player, "MINE:ALL", + getCounter(player, "MINE:ALL") + + 1); + } + public boolean onReportVillage( + String player, + String worldName, + double x, double y, double z) { + String dbKey = "village_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + Object lastObj = + storage.getCondition( + player, dbKey); + String lastPos = lastObj != null + ? lastObj.toString() : ""; + if (curPos.equals(lastPos)) { + return false; + } + storage.setConditionRaw( + player, dbKey, curPos); + + String key = "REPORT_VILLAGE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + return true; + } + + public void onMobKill( + String player, String mobType) { + String key = "KILL:" + mobType; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayerKill( + String player) { + String key = "KILL_PLAYER"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onItemCrafted( + String player) { + String key = "CRAFT"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onFishCaught( + String player) { + String key = "FISH"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayTime( + String player, long ms) { + String key = "PLAYTIME"; + long cur = getCounterLong( + player, key); + setCounter(player, key, cur + ms); + } + + // ========== 内部:条件判定 ========== + + private boolean isStageComplete( + String player, + List conditions) { + if (conditions == null + || conditions.isEmpty()) + return true; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) + return false; + } + return true; + } + + public boolean isConditionMet( + String player, + String rawCond) { + if (player == null + || rawCond == null) + return false; + + String key = stripOptional(rawCond); + + // 无条件类文本直接通过 + if (key.equals("无前级条件") + || key.equals("无条件") + || key.equals("无") + || key.equals("none") + || key.equals("无需条件") + || key.equals("无解锁条件")) { + return true; + } + + if (key.contains("打卡") + && (key.contains("群系") + || key.contains("风景点"))) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return total >= 1; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return catCount >= 1; + } + return getCounter( + player, "biome_ALL") >= 1; + } + + if (key.contains("完成") + && key.contains("阶段")) { + return checkStageDependency( + player, key); + } + + if ("完成注册登录".equals(key)) { + return getDb() != null + && getDb().userExists( + player); + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + return val != null + && !val.toString().isEmpty(); + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return cur >= needed * 3600000L; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + return getCounter( + player, "CHAT") >= 1; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return total >= needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return streak >= needed; + } + + if (key.startsWith("MINE:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "MINE:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "KILL:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "VILLAGER_TRADE") + >= needed; + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter(player, + "KILL_PLAYER") >= n; + } + + if (key.startsWith("CRAFT")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "CRAFT") >= n; + } + + if (key.startsWith("FISH")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "FISH") >= n; + } + + if (key.startsWith("PLAYTIME")) { + String[] p = key.split(":"); + long n = p.length >= 2 + ? Long.parseLong(p[1]) : 0; + return getCounterLong(player, + "PLAYTIME") >= n; + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "REPORT_VILLAGE") + >= needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + return getCounter( + player, "REPORT_VILLAGE") + >= 1; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return streak >= needed; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + // ===== 泛解析:击杀类中文条件 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + for (String mobType : mobTypes) { + total += getCounter(player, + "KILL:" + mobType); + } + return total >= needed; + } + } + + // ===== 泛解析:村民交易条件 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "TRADE") + >= needed; + } + + // ===== 泛解析:采集/挖矿类条件 ===== + if (key.contains("采集") || key.contains("挖")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + for (String mat : matTypes) { + total += getCounter(player, + "MINE:" + mat); + } + return total >= needed; + } + } + + // ===== 泛解析:合成类条件 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "CRAFT") + >= needed; + } + + // ===== 泛解析:钓鱼类条件 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "FISH") + >= needed; + } + + return false; + } + + public void debugLogin(String player) { + try { + var user = getDb().getUser(player); + if (user != null) { + plugin.getLogger().info( + "[QuestTracker] " + + "=== " + player + + " 数据库全部字段 ==="); + for (var entry + : user.entrySet()) { + plugin.getLogger().info( + "[QuestTracker] " + + entry.getKey() + + " = " + + entry.getValue() + + " (" + + (entry.getValue() + != null + ? entry.getValue() + .getClass() + .getSimpleName() + : "null") + + ")"); + } + } else { + plugin.getLogger().info( + "[QuestTracker] " + + "getUser返回null: " + + player); + } + } catch (Exception e) { + plugin.getLogger().info( + "[QuestTracker] " + + "debug异常: " + + e.getMessage()); + } + } + + // ========== 内部:计数器 ========== + + private int getCounter( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + return 0; + } + private int getIntField( + String player, String field) { + try { + Object val = getDb().getField( + player, field); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val != null) { + return Integer.parseInt( + val.toString().trim()); + } + } catch (Exception e) { + // 忽略 + } + return 0; + } + + public void onVillagerTrade(String player) { + String key = "VILLAGER_TRADE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + private long getCounterLong( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).longValue(); + } + return 0; + } + + private void setCounter( + String player, String key, + int value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + private void setCounter( + String player, String key, + long value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + // ========== 工具 ========== + + private boolean toBool(Object val) { + if (val == null) return false; + if (val instanceof Boolean) + return (Boolean) val; + if (val instanceof Number) + return ((Number) val) + .intValue() != 0; + String s = String.valueOf(val); + return "1".equals(s) + || "true" + .equalsIgnoreCase(s); + } + + public boolean isOptional(String cond) { + if (cond == null) return false; + String c = cond; + return c.contains("(进阶)") + || c.contains("(可选)") + || c.contains(",可选)") + || c.contains(",可选)") + || c.contains("【可选】") + || c.contains("【进阶】"); + } + + + private String stripOptional(String cond) { + if (cond == null) return ""; + return cond.replace("(进阶)", "") + .replace("(可选)", "") + .replace(",可选)", "") + .replace(",可选)", "") + .replace("【可选】", "") + .replace("【进阶】", "") + .trim(); + } + + private int parseInt(String s) { + + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return 0; + } + } + private Object getDbField( + String player, String field) { + Main main = (Main) plugin; + return main.getDb().getField( + player, field); + } + + /** + * 从文本中提取第一个数字 + * "在线时间≥1小时" → 1 + * "破坏/放置方块100个" → 100 + */ + private int parseNumberFromText( + String text) { + java.util.regex.Matcher m = + java.util.regex.Pattern + .compile("(\\d+)") + .matcher(text); + if (m.find()) { + try { + return Integer.parseInt( + m.group(1)); + } catch (Exception e) { + return 0; + } + } + return 0; + } + public void onBlockPlaced( + String player, String material) { + String key = "PLACE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + setCounter(player, "PLACE:ALL", + getCounter(player, "PLACE:ALL") + + 1); + } + + public void onPlayerChat( + String player) { + String key = "CHAT"; + setCounter(player, key, + getCounter(player, key) + 1); + } + + private String formatMs(long ms) { + long sec = ms / 1000; + long min = sec / 60; + long hr = min / 60; + if (hr > 0) + return hr + "小时" + + (min % 60) + "分"; + if (min > 0) + return min + "分" + + (sec % 60) + "秒"; + return sec + "秒"; + } + + // ========== 数据结构 ========== + + public static class QuestFile { + public String displayName = ""; + public String fileName = ""; + public List details = + new ArrayList<>(); + public List rewards = + new ArrayList<>(); + public List conditions = + new ArrayList<>(); + + public boolean hasConditions() { + return conditions != null + && !conditions.isEmpty(); + } + } +} diff --git a/backup/src/main/java/Sdf1_login/RadioDownloadListener.java b/backup/src/main/java/Sdf1_login/RadioDownloadListener.java new file mode 100644 index 0000000..d3e744f --- /dev/null +++ b/backup/src/main/java/Sdf1_login/RadioDownloadListener.java @@ -0,0 +1,144 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player + .PlayerResourcePackStatusEvent; + +import java.util.UUID; +import java.util.concurrent + .ConcurrentHashMap; + +public class RadioDownloadListener + implements Listener { + + private final Main plugin; + private final java.util.logging.Logger log; + + private final ConcurrentHashMap + failCount = new ConcurrentHashMap<>(); + private final ConcurrentHashMap + blocked = new ConcurrentHashMap<>(); + + private static final int MAX_FAILED = 3; + + public RadioDownloadListener(Main plugin) { + this.plugin = plugin; + this.log = plugin.getLogger(); + } + + public void onPlayerQuit(UUID uuid) { + failCount.remove(uuid); + blocked.remove(uuid); + } + + @EventHandler + public void onStatus( + PlayerResourcePackStatusEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + String status = + e.getStatus().name(); + + log.info("[Radio] === 资源包状态 ==="); + log.info("[Radio] 玩家: " + + p.getName()); + log.info("[Radio] UUID: " + uuid); + log.info("[Radio] 状态: " + + status); + log.info("[Radio] 原始状态枚举: " + + e.getStatus().getClass() + .getSimpleName() + + "." + + e.getStatus().name()); + + if (blocked.containsKey(uuid)) { + log.info("[Radio] 已熔断,跳过: " + + p.getName()); + return; + } + + switch (status) { + case "SUCCESS": + log.info("[Radio] " + + p.getName() + + " ✓ 资源包加载成功"); + failCount.remove(uuid); + blocked.remove(uuid); + break; + + case "ACCEPTED": + log.info("[Radio] " + + p.getName() + + " 客户端已接受," + + "等待下载..."); + break; + + case "DECLINED": + log.warning("[Radio] " + + p.getName() + + " ✗ 拒绝了资源包"); + log.warning("[Radio] " + + "可能原因: " + + "1.客户端设置禁用资源包" + + " 2.已缓存拒绝记录" + + " 3.SSL证书不匹配"); + handleFailure(p, uuid); + break; + + case "FAILED_DOWNLOAD": + log.warning("[Radio] " + + p.getName() + + " ✗ 下载失败"); + log.warning("[Radio] " + + "可能原因: " + + "1.URL无法访问" + + " 2.SSL证书错误" + + " 3.网络超时" + + " 4.文件不存在(404)"); + handleFailure(p, uuid); + break; + + default: + log.info("[Radio] " + + p.getName() + + " 未知状态: " + + status); + break; + } + } + + private void handleFailure(Player p, UUID uuid) { + int failNum = failCount.getOrDefault(uuid, 0) + 1; + failCount.put(uuid, failNum); + + log.warning("[Radio] " + p.getName() + + " 失败次数: " + failNum + "/" + MAX_FAILED); + + if (failNum >= MAX_FAILED) { + blocked.put(uuid, true); + log.severe("[Radio] " + p.getName() + + " 已达最大失败次数,停止重试," + + "玩家继续留在服务器"); + // 不踢出,只记录熔断,玩家正常游玩 + p.sendMessage("§e§l[Radio] 资源包加载失败" + + ",已取消下载资源包,部分画面音频可能出现问题" + + "\n§d§l若想继续下载,请退出重进。若持续失败,请上报管理员"); + + return; + } +// 打了包没测试 + p.sendMessage("§e§l[Radio] 下载失败," + + "重试... (" + failNum + "/" + MAX_FAILED + ")"); + + // 5秒后重试 + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline() && plugin.radio != null) { + plugin.radio.sendResourcePack(p); + } + }, 100L); + } +} \ No newline at end of file diff --git a/backup/src/main/java/Sdf1_login/RadioManager.java b/backup/src/main/java/Sdf1_login/RadioManager.java new file mode 100644 index 0000000..6fe5eda --- /dev/null +++ b/backup/src/main/java/Sdf1_login/RadioManager.java @@ -0,0 +1,1040 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.zip.*; +import java.util.Collections; +import org.bukkit.NamespacedKey; +import java.util.Set; + + +public class RadioManager { + + private final Main plugin; + private File radioDir; + private File oggDir; + private File packDir; + private BukkitTask mainRadioTask; + private final Map loginPlayers = + new ConcurrentHashMap<>(); + private String externalUrl = ""; + private int httpPort = 443; + private final Set pendingPack = + Collections.newSetFromMap( + new ConcurrentHashMap<>()); + private String lastPlayedSound = ""; + + private int radioIndex = 0; + + // ========== 登录阶段推送(管理员手动触发) ========== + public void startLoginRadio(Player p) { + loginPlayers.put(p.getName(), true); + } + + public void stopLoginRadio(Player p) { + loginPlayers.remove(p.getName()); + } + + public void stopAllLoginRadio() { + loginPlayers.clear(); + } + + private final List mainFiles = new ArrayList<>(); + private final List loginFiles = new ArrayList<>(); + + public RadioManager(Main plugin) { + this.plugin = plugin; + this.radioDir = new File( + plugin.getDataFolder(), "radio"); + this.oggDir = new File( + plugin.getDataFolder(), "radio/ogg"); + this.packDir = new File( + plugin.getDataFolder(), "radiopack"); + radioDir.mkdirs(); + oggDir.mkdirs(); + packDir.mkdirs(); + loadRadioConfig(); + } + // ========== 读取 radio_config.txt ========== + private void loadRadioConfig() { + File cfg = new File( + plugin.getDataFolder(), + "radio_config.txt"); + + // 不存在则创建默认配置 + if (!cfg.exists()) { + createRadioConfig(cfg); + plugin.getLogger().info( + "[Radio] 已生成 radio_config.txt"); + } + + // 读取 + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(cfg), + java.nio.charset + .StandardCharsets + .UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("#") + || line.trim().isEmpty()) + continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String key = line.substring(0, eq) + .trim(); + String val = line.substring(eq + 1) + .trim(); + if (key.equals("resource-pack-url")) { + externalUrl = val; + } else if (key.equals("http-port")) { + try { + httpPort = Integer.parseInt( + val); + } catch (NumberFormatException ignored) { + } + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 读取radio_config.txt失败: " + + e.getMessage()); + } + + // 443是HTTPS端口,内嵌HTTP不能用 + if (httpPort == 443) httpPort = 8080; + + plugin.getLogger().info( + "[Radio] 配置: externalUrl=[" + + externalUrl + "] httpPort=" + + httpPort); + } + + private void createRadioConfig(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + java.nio.charset + .StandardCharsets + .UTF_8))) { + pw.println("# ============ Sdf1 Radio Config ============"); + pw.println("#"); + pw.println("# resource-pack-url"); + pw.println("# 填外部地址(HTTP或HTTPS均可)"); + pw.println("# 例: https://example.com/radiopack.zip"); + pw.println("# 留空 = 使用内置HTTP服务"); + pw.println("#"); + pw.println("# http-port"); + pw.println("# 内置HTTP端口(仅resource-pack-url为空时生效)"); + pw.println("#"); + pw.println("# ============ Settings ==============="); + pw.println(); + pw.println("resource-pack-url="); + pw.println("http-port=8080"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 创建radio_config.txt失败: " + + e.getMessage()); + } + } + + // ========== 初始化 ========== + public void init() { + loadRadioConfig(); + convertNonOgg(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); // 启动HTTP + plugin.getLogger().info( + "[Radio] 初始化完成: 全服=" + + mainFiles.size() + + " 登录=" + loginFiles.size()); + } + + + // ========== 转换非ogg文件 ========== + private void convertNonOgg() { + convertDir(radioDir); + convertDir(new File(radioDir, "login")); + convertDir(new File(radioDir, "reg")); + } + + private void convertDir(File src) { + if (!src.exists() || !src.isDirectory()) return; + File[] files = src.listFiles((d, n) -> { + String l = n.toLowerCase(); + return l.endsWith(".mp3") + || l.endsWith(".wav") + || l.endsWith(".flac"); + }); + if (files == null) return; + + for (File f : files) { + String oggName = f.getName() + .replaceAll("\\.(mp3|wav|flac)$", + ".ogg"); + File oggFile = new File(src, oggName); + if (oggFile.exists() + && oggFile.lastModified() + >= f.lastModified()) { + continue; + } + // 调用ffmpeg转换 + if (convertWithFfmpeg(f, oggFile)) { + plugin.getLogger().info( + "[Radio] 转换成功: " + + f.getName()); + } else { + plugin.getLogger().warning( + "[Radio] 转换失败: " + + f.getName() + + " (需要安装ffmpeg)"); + } + } + } + + private boolean convertWithFfmpeg( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-c:a", "libvorbis", + "-q:a", "5", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean finished = + p.waitFor(30, TimeUnit.SECONDS); + if (!finished) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + return false; + } + } + + // ========== 加载ogg文件列表 ========== + private void loadAllOggs() { + mainFiles.clear(); + loginFiles.clear(); + loadOggsFlat(radioDir, mainFiles); + loadOggsFlat( + new File(radioDir, "login"), + loginFiles); + loadOggsFlat( + new File(radioDir, "reg"), + loginFiles); + } + + private void loadOggsFlat( + File dir, List list) { + if (!dir.exists() || !dir.isDirectory()) + return; + File[] files = dir.listFiles( + (d, n) -> n.toLowerCase() + .endsWith(".ogg")); + if (files == null) return; + Arrays.sort(files); + for (File f : files) list.add(f); + } + /** + * 解析 pack.png:优先读取插件目录, + * 没有则搜索图片并用ffmpeg转码 + */ + private File resolvePackIcon() { + File dataDir = plugin.getDataFolder(); + File packPng = new File(dataDir, "pack.png"); + + if (packPng.exists() + && packPng.length() > 0) { + plugin.getLogger().info( + "[Radio] 找到 pack.png (" + + packPng.length() + + " bytes)"); + return packPng; + } + + // 搜索可转换的图片文件 + String[] exts = {"png", "jpg", "jpeg", + "bmp", "webp", "tiff", "gif"}; + File[] found = dataDir.listFiles( + (dir, name) -> { + String low = name.toLowerCase(); + for (String ext : exts) { + if (low.endsWith("." + ext) + && !name.equals( + "pack.png")) { + return true; + } + } + return false; + }); + + if (found != null && found.length > 0) { + File src = found[0]; + plugin.getLogger().info( + "[Radio] 找到图片: " + + src.getName() + + " → 转码为 pack.png"); + if (convertToPackPng(src, packPng)) { + return packPng; + } + plugin.getLogger().warning( + "[Radio] 转码失败"); + return null; + } + + plugin.getLogger().warning( + "[Radio] 未找到 pack.png 或可转换图片"); + return null; + } + + /** + * ffmpeg 将任意图片转为 256x256 PNG + */ + private boolean convertToPackPng( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-vf", + "scale='min(256,iw)'" + + ":'min(256,ih)':" + + "force_original_aspect_ratio" + + "=decrease," + + "pad=256:256:" + + "(ow-iw)/2:" + + "(oh-ih)/2:" + + "color=0x00000000", + "-c:v", "png", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean ok = p.waitFor( + 30, TimeUnit.SECONDS); + if (!ok) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] ffmpeg转码失败: " + + e.getMessage()); + return false; + } + } + + // ========== 资源包构建 ========== + private void buildResourcePack() { + deleteDir(packDir); + packDir.mkdirs(); + + List allOggs = new ArrayList<>(); + collectOggs(radioDir, allOggs); + + if (allOggs.isEmpty()) { + plugin.getLogger().warning( + "[Radio] radio/ 目录下" + + "无 .ogg 文件,跳过资源包"); + return; + } + + // 正确路径:assets/minecraft/sounds/custom/ + File soundsDir = new File(packDir, + "assets/minecraft/sounds/custom"); + soundsDir.mkdirs(); + + // 生成 sounds.json + StringBuilder json = + new StringBuilder("{\n"); + for (int i = 0; i < allOggs.size(); i++) { + File ogg = allOggs.get(i); + String simpleName = + ogg.getName() + .replace(".ogg", ""); + File dest = new File(soundsDir, + simpleName + ".ogg"); + try { + Files.copy(ogg.toPath(), + dest.toPath(), + StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + // ignore + } + // 音频事件名: custom.xxx + // 文件路径: custom/xxx + json.append(" \"custom.") + .append(simpleName) + .append("\": {\n"); + json.append(" \"sounds\": [{\n"); + json.append(" \"name\":" + + " \"custom/") + .append(simpleName) + .append("\",\n"); + json.append( + " \"stream\": true\n"); + json.append(" }]\n"); + json.append(" }"); + if (i < allOggs.size() - 1) + json.append(","); + json.append("\n"); + } + json.append("}"); + + // sounds.json 放在 assets/minecraft/ 下 + try { + Files.write(new File(packDir, + "assets/minecraft/sounds.json") + .toPath(), + json.toString() + .getBytes("UTF-8")); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] sounds.json写入失败"); + } +// sounds.json 写入后... + +// ===== 新增:打包 pack.png 作为资源包图标 ===== + File iconPng = resolvePackIcon(); + if (iconPng != null && iconPng.exists()) { + try { + Files.copy(iconPng.toPath(), + new File(packDir, "pack.png") + .toPath(), + StandardCopyOption + .REPLACE_EXISTING); + plugin.getLogger().info( + "[Radio] pack.png 已加入资源包"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] pack.png复制失败: " + + e.getMessage()); + } + } + + // pack.mcmeta(pack_format=54) + try { + Files.write(new File(packDir, + "pack.mcmeta").toPath(), + ("{\n \"pack\": {\n" + + " \"pack_format\": 54,\n" + + " \"description\":" + + " \"§d§l草原探险服务器资源包,请勿拒绝资源包\"\n }\n}") + .getBytes("UTF-8")); + } catch (Exception e) { + // ignore + } + + try { + File zipFile = new File( + plugin.getDataFolder(), + "radiopack.zip"); + packZip(packDir, zipFile); + plugin.getLogger().info( + "[Radio] 资源包生成: " + + allOggs.size() + " 个音频"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] zip打包失败: " + + e.getMessage()); + } + } + + private void deleteDir(File dir) { + if (!dir.exists()) return; + File[] files = dir.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) deleteDir(f); + else f.delete(); + } + } + dir.delete(); + } + + private void collectOggs( + File dir, List list) { + File[] files = dir.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) + collectOggs(f, list); + else if (f.getName().endsWith(".ogg")) + list.add(f); + } + } + + private void packZip(File root, + File zipFile) throws Exception { + try (ZipOutputStream zos = + new ZipOutputStream( + new FileOutputStream( + zipFile))) { + zipDir(root, root, zos); + } + } + + private void zipDir(File root, + File current, + ZipOutputStream zos) + throws Exception { + File[] files = current.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) { + zipDir(root, f, zos); + } else { + String path = root.toURI() + .relativize(f.toURI()) + .getPath(); + zos.putNextEntry(new ZipEntry(path)); + zos.write(Files.readAllBytes( + f.toPath())); + zos.closeEntry(); + } + } + } + + // ========== 资源包发送 ========== + + /** + * 发送资源包(带限流,不禁重试) + */ + private final Map lastSendTime = + new ConcurrentHashMap<>(); + + public void sendResourcePack(Player p) { + UUID uuid = p.getUniqueId(); + + // 最短间隔 5秒(不是10秒的全锁死) + Long last = lastSendTime.get(uuid); + if (last != null + && System.currentTimeMillis() - last + < 5000L) { + plugin.getLogger().info( + "[Radio] 跳过(5秒内): " + + p.getName()); + return; + } + lastSendTime.put(uuid, + System.currentTimeMillis()); + + String url = getResourcePackUrl(); + if (url == null || url.isEmpty()) { + plugin.getLogger().warning( + "[Radio] URL为空,跳过: " + + p.getName()); + return; + } + + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) { + plugin.getLogger().warning( + "[Radio] radiopack.zip不存在," + + "跳过: " + p.getName()); + return; + } + + try { + byte[] data = + Files.readAllBytes(zip.toPath()); + String hash = sha1(data); + long fileSize = zip.length(); + + plugin.getLogger().info( + "[Radio] 发送: " + p.getName() + + " url=" + url + + " size=" + fileSize + + " hash=" + hash); + + sendPackWithHash(p, url, hash); + + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] 发送异常: " + + p.getName()); + e.printStackTrace(); + } + } + + + + private void sendPackWithHash( + Player p, + String url, + String hash) { + + UUID uuid = UUID.fromString( + "a1b2c3d4-e5f6-7890-abcd-" + + "ef1234567890"); + + // 尝试5参 + Component + try { + Object component = + net.kyori.adventure.text + .Component.text( + "§d§l请下载自定义资源包"); + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + net.kyori.adventure + .text.Component + .class, + boolean.class) + .invoke( + p, uuid, url, + hash, component, + true); + plugin.getLogger().info( + "[Radio] 成功(5参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试5参 + String描述 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + String.class, + boolean.class) + .invoke( + p, uuid, url, + hash, + "Sdf1 Radio", + true); + plugin.getLogger().info( + "[Radio] 成功(5参String): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试3参 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class) + .invoke( + p, uuid, url, + hash); + plugin.getLogger().info( + "[Radio] 成功(3参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // ★ 全部失败 → 通知玩家 + 熔断 + plugin.getLogger().severe( + "[Radio] 所有签名失败: " + + p.getName() + + " 客户端版本: " + + p.getProtocolVersion()); + p.sendMessage( + "§e§l[Radio] §f资源包发送失败," + + "§7部分功能可能异常"); + p.sendMessage( + "§7可尝试: §e/rejoin §7或重启客户端"); + } + + + + private void sendPackFromLocal( + Player p, String url) { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) return; + try { + byte[] data = + Files.readAllBytes( + zip.toPath()); + String hash = sha1(data); + sendPackWithHash(p, url, hash); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 降级发送失败"); + } + } + + // 从URL下载字节 + private byte[] downloadBytes(String urlStr) + throws Exception { + java.net.URL url = + new java.net.URL(urlStr); + java.net.HttpURLConnection conn = + (java.net.HttpURLConnection) + url.openConnection(); + conn.setConnectTimeout(10000); + conn.setReadTimeout(30000); + try (java.io.InputStream is = + conn.getInputStream()) { + return is.readAllBytes(); + } + } + + // SHA-1计算 + private String sha1(byte[] data) + throws Exception { + java.security.MessageDigest md = + java.security.MessageDigest + .getInstance("SHA-1"); + md.update(data); + byte[] hash = md.digest(); + StringBuilder sb = + new StringBuilder(); + for (byte b : hash) + sb.append(String.format( + "%02x", b)); + return sb.toString(); + } + + public void clearPending(UUID uuid) { + lastSendTime.remove(uuid); + } + + + + // ========== 内嵌HTTP服务 ========== + private com.sun.net.httpserver.HttpServer + httpServer; + + private void startHttpServer() { + // 如果配置了外部地址则不启动 + if (plugin.getConfig2() != null + && !plugin.getConfig2() + .broadcastServerUrl + .isEmpty()) { + return; + } + try { + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + httpServer = + com.sun.net.httpserver + .HttpServer.create( + new java.net + .InetSocketAddress( + port), 0); + httpServer.createContext( + "/radiopack.zip", + exchange -> { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (zip.exists()) { + byte[] data = + Files.readAllBytes( + zip.toPath()); + exchange.getResponseHeaders() + .set("Content-Type", + "application/zip"); + exchange.getResponseHeaders() + .set("Access-Control" + + "-Allow-Origin", + "*"); + exchange.sendResponseHeaders( + 200, data.length); + exchange.getResponseBody() + .write(data); + exchange.getResponseBody() + .close(); + } else { + byte[] empty = + "not found".getBytes(); + exchange.sendResponseHeaders( + 404, empty.length); + exchange.getResponseBody() + .write(empty); + exchange.getResponseBody() + .close(); + } + }); + httpServer.setExecutor(null); + httpServer.start(); + plugin.getLogger().info( + "[Radio] HTTP服务: http://0.0.0.0:" + + port + "/radiopack.zip"); + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] HTTP启动失败: " + + e.getMessage()); + } + } + + private void stopHttpServer() { + if (httpServer != null) { + httpServer.stop(0); + httpServer = null; + } + } + + public String getResourcePackUrl() { + // 有外部配置 → 随机生成URL + if (externalUrl != null + && !externalUrl.trim().isEmpty()) { + return generateRandomUrl(externalUrl); + } + // 无配置 → 用内置HTTP + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + return "http://127.0.0.1:" + + port + "/radiopack.zip"; + } + + + /** + * 从配置的域名列表中随机选一个 + * 生成随机、带时间戳的完整资源包URL + * + * 规则: + * *.example.com + * → 随机子域+时间戳 + * 例: a3f7k2.example.com/pack.zip?t=xxx&v=b9c1e4 + * + * cdn.example.com (无*) + * → 直接使用+时间戳 + * 例: cdn.example.com/pack.zip?t=xxx + */ + private String generateRandomUrl( + String raw) { + String[] parts = raw.split("\\|"); + String pick = + parts[(int)(Math.random() + * parts.length)].trim(); + + // 去掉协议 https:// http:// + String cleaned = pick + .replaceFirst("^https?://", ""); + // 去掉路径 /path/a?v=1 + if (cleaned.contains("/")) { + cleaned = cleaned.substring( + 0, cleaned.indexOf('/')); + } + + // 随机子域 + String hex = Long.toHexString( + Double.doubleToLongBits( + Math.random())); + String rand = hex.length() > 6 + ? hex.substring(2, 8) : hex; + rand = rand.replaceAll( + "[^a-z0-9]", "x"); + + String host; + if (cleaned.startsWith("*")) { + host = rand + + cleaned.substring(1); + } else { + host = cleaned; + } + + String ts = String.valueOf( + System.currentTimeMillis()); + String token = Integer.toHexString( + (int)(Math.random() + * 0xFFFFFF)); + + String url = "https://" + host + + "/radiopack.zip" + + "?t=" + ts + + "&v=" + token; + + plugin.getLogger().info( + "[Radio] 生成URL: " + + pick + " → " + url); + return url; + } + + + + // ========== 全服广播 ========== + public void startMainRadio() { + stopMainRadio(); + if (mainFiles.isEmpty()) { + plugin.getLogger().warning( + "[Radio] 无 .ogg 文件"); + return; + } + plugin.getLogger().info( + "[Radio] 启动全服广播 (" + + mainFiles.size() + " 个文件)"); + mainRadioTask = new BukkitRunnable() { + @Override + public void run() { + playNext(); + } + }.runTaskTimer(plugin, 100L, 2400L); + } + + private void playNext() { + if (mainFiles.isEmpty()) return; + File file = mainFiles.get( + radioIndex % mainFiles.size()); + String name = file.getName() + .replace(".ogg", ""); + lastPlayedSound = "custom." + name; + String key = "minecraft:" + lastPlayedSound; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + radioIndex++; + } + + // ========== 按名称播放 ========== + public void playToAll(String name) { + String key = findSongKey(name); + if (key == null) return; + lastPlayedSound = "custom." + name; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + } + + + public void playToPlayer( + Player p, String name) { + String key = findSongKey(name); + if (key == null) { + p.sendMessage("§c未找到: " + name); + return; + } + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + + public void playToLoginPlayers(String name) { + String key = findSongKey(name); + if (key == null) return; + for (String pName + : loginPlayers.keySet()) { + Player lp = Bukkit.getPlayer(pName); + if (lp != null && lp.isOnline()) { + lp.playSound(lp.getLocation(), + key, + SoundCategory.RECORDS, + 1.5f, 1.0f); + } + } + } + + + private String findSongKey(String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + return "minecraft:custom." + name; + } + } + plugin.getLogger().warning( + "[Radio] 未找到歌曲: " + name); + return null; + } + + + public boolean hasSong(String name) { + return findSongKey(name) != null; + } + + // ========== 手动播放 ========== + public boolean playSpecific( + Player sender, String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + String key = "sdf1radio:" + name; + for (Player p + : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), + key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + + name); + return true; + } + } + if (sender != null) + sender.sendMessage("§c未找到: " + name); + return false; + } + public void stopMainRadio() { + if (mainRadioTask != null) { + mainRadioTask.cancel(); + mainRadioTask = null; + } + if (lastPlayedSound != null + && !lastPlayedSound.isEmpty()) { + for (Player p : Bukkit.getOnlinePlayers()) { + p.stopSound(lastPlayedSound, + SoundCategory.RECORDS); + } + lastPlayedSound = ""; + } + } + +// 在 init() 或 registerEvents 中保存引用: +// this.listener = new RadioDownloadListener(plugin); +private RadioDownloadListener radioListener; + + + + // ========== 重载 ========== + public void reload() { + stopMainRadio(); + stopAllLoginRadio(); + stopHttpServer(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); + loadRadioConfig(); + } +} \ No newline at end of file diff --git a/backup/src/main/java/Sdf1_login/RefundRecord.java b/backup/src/main/java/Sdf1_login/RefundRecord.java new file mode 100644 index 0000000..c0a3070 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/RefundRecord.java @@ -0,0 +1,62 @@ +package Sdf1_login; + +import org.bukkit.Material; +import java.util.UUID; + +public class RefundRecord { + public final UUID playerUUID; + public final String playerName; + public final String itemId; + public final String itemName; + public final Material material; + public final Material shulkerMaterial; + public int amount; + public int totalPaid; + public final long timestamp; + + // 普通购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, int amount, + int totalPaid, long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = null; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + // 盒装购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, + Material shulkerMat, + int amount, int totalPaid, + long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = shulkerMat; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + public boolean isExpired() { + return System.currentTimeMillis() + - timestamp > 300000L; + } + + public int getSecondsLeft() { + long left = 300000L + - (System.currentTimeMillis() + - timestamp); + return (int) Math.max(0, left / 1000); + } +} diff --git a/backup/src/main/java/Sdf1_login/RiskControlManager.java b/backup/src/main/java/Sdf1_login/RiskControlManager.java new file mode 100644 index 0000000..4ddedcc --- /dev/null +++ b/backup/src/main/java/Sdf1_login/RiskControlManager.java @@ -0,0 +1,248 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 风控管理器:检测异常登录行为,记录尝试次数,触发安全报警 + */ +public class RiskControlManager { + + private final Main plugin; + + // 每个玩家名 -> 连续失败次数 + private final Map failAttempts = + new ConcurrentHashMap<>(); + + // 每个IP -> 关联的可疑玩家列表 + private final Map> ipSuspicious = + new ConcurrentHashMap<>(); + + // 每个玩家 -> 上次验证时间(用于冷却) + private final Map verifyCooldown = + new ConcurrentHashMap<>(); + + // 配置项 + private static final int MAX_FAIL_ATTEMPTS = 5; + private static final int MAX_ACCOUNTS_PER_IP = 5; + private static final long COOLDOWN_MS = 60000L; + + public RiskControlManager(Main plugin) { + this.plugin = plugin; + } + + /** + * 记录一次失败的登录尝试 + * + * @param playerName 玩家名 + * @return 剩余尝试次数 + */ + public int recordFailAttempt(String playerName) { + int current = failAttempts + .getOrDefault(playerName, 0) + 1; + failAttempts.put(playerName, current); + return Math.max(0, MAX_FAIL_ATTEMPTS - current); + } + + /** + * 重置指定玩家的失败计数(登录成功时调用) + */ + public void resetFailAttempts(String playerName) { + failAttempts.remove(playerName); + } + + /** + * 获取指定玩家的失败次数 + */ + public int getFailAttempts(String playerName) { + return failAttempts + .getOrDefault(playerName, 0); + } + + /** + * 判断玩家是否已被锁定(失败次数达到上限) + */ + public boolean isLocked(String playerName) { + return failAttempts + .getOrDefault(playerName, 0) + >= MAX_FAIL_ATTEMPTS; + } + + /** + * 检查IP是否达到注册上限 + * 直接查数据库,不依赖 DatabaseManager 的方法 + */ + public boolean canRegisterFromIP(String ip) { + if (ip == null || ip.isEmpty()) return true; + try { + java.sql.Connection conn = + plugin.getDb().getDb(); + if (conn == null) return true; + java.sql.PreparedStatement ps = + conn.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address = ?"); + ps.setString(1, ip); + java.sql.ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count < 5; + } catch (Exception e) { + e.printStackTrace(); + return true; + } + } + + + /** + * 记录IP关联的可疑行为 + */ + public void recordSuspiciousIP(String ip, + String playerName) { + if (ip == null || ip.isEmpty()) return; + ipSuspicious + .computeIfAbsent(ip, + k -> ConcurrentHashMap.newKeySet()) + .add(playerName); + } + + /** + * 获取某IP关联的所有可疑玩家 + */ + public Set getSuspiciousAccounts(String ip) { + return ipSuspicious + .getOrDefault(ip, Collections.emptySet()); + } + + /** + * 检查是否在验证冷却期内 + */ + public boolean isOnCooldown(String playerName) { + Long last = verifyCooldown.get(playerName); + if (last == null) return false; + return System.currentTimeMillis() - last + < COOLDOWN_MS; + } + + /** + * 设置验证冷却时间 + */ + public void setCooldown(String playerName) { + verifyCooldown.put(playerName, + System.currentTimeMillis()); + } + + /** + * 当检测到可疑登录时触发报警 + * - 全服广播 + * - 有邮箱则发邮件 + * - 记录到数据库 + */ + public void triggerAlert(Player player, + String suspiciousIP) { + String name = player.getName(); + + // 记录到数据库 + plugin.getDb().recordSecurityAlert( + name, suspiciousIP); + + // 记录IP可疑行为 + recordSuspiciousIP(suspiciousIP, name); + + // 全服广播 + String serverMsg = "§c§l[安全警告] §e" + name + + " §f的账号疑似遭到 §c" + suspiciousIP + + " §f的盗号尝试, 请各位玩家注意账号安全!"; + Bukkit.broadcastMessage(serverMsg); + + // 有邮箱则发邮件 + String emailAddr = (String) plugin.getDb() + .getField(name, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String subject = + "[安全警告] 您的账号疑似遭到盗号尝试"; + String body = "尊敬的玩家 " + name + + ":\n\n" + + "您的账号在 " + + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()) + + " 疑似遭到 IP: " + suspiciousIP + + " 的盗号尝试。\n\n" + + "建议您:\n" + + "1. 立即修改密码" + + "(使用 /sdf1_login pw)\n" + + "2. 如非本人操作," + + "请联系管理员处理\n\n" + + "--Sdf1_login 安全系统"; + plugin.getEmail().sendTempPassword( + emailAddr, name, body); + } + } + + /** + * 处理登录失败时的风控逻辑 + * + * @param player 玩家 + * @param suspiciousIP 可疑来源IP + * @return true=已被锁定(应踢出), false=仍可继续尝试 + */ + public boolean handleLoginFailure(Player player, + String suspiciousIP) { + String name = player.getName(); + int remaining = recordFailAttempt(name); + + if (remaining <= 0) { + // 达到上限,触发报警 + triggerAlert(player, suspiciousIP); + player.kickPlayer( + "§c§l[风控] §f登录失败次数过多," + + " 账号已被临时锁定, 请联系管理员"); + return true; + } + + // 未达上限,提示剩余次数 + player.sendMessage( + "§c§l[风控] §f密码错误! " + + "剩余" + remaining + "次机会"); + return false; + } + + /** + * 登录成功后重置风控状态 + */ + public void onLoginSuccess(String playerName) { + resetFailAttempts(playerName); + } + + /** + * 清理过期的冷却记录(可由定时任务调用) + */ + public void cleanup() { + long now = System.currentTimeMillis(); + verifyCooldown.entrySet().removeIf( + e -> now - e.getValue() > COOLDOWN_MS * 10); + } + + /** + * 获取所有玩家的失败尝试记录(调试用) + */ + public Map getAllFailAttempts() { + return Collections.unmodifiableMap(failAttempts); + } + + /** + * 清除所有风控数据(管理员调试用) + */ + public void clearAll() { + failAttempts.clear(); + ipSuspicious.clear(); + verifyCooldown.clear(); + } +} diff --git a/backup/src/main/java/Sdf1_login/SalesStatsManager.java b/backup/src/main/java/Sdf1_login/SalesStatsManager.java new file mode 100644 index 0000000..9d06a22 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/SalesStatsManager.java @@ -0,0 +1,170 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class SalesStatsManager { + + private final Main plugin; + + private final Map sessionSales = + new ConcurrentHashMap<>(); + private int sessionRevenue = 0; + private int sessionOrders = 0; + private int sessionItems = 0; + private int sessionRefunds = 0; + private int sessionRefundAmount = 0; + private long sessionStart = System.currentTimeMillis(); + + // 状态 + private boolean waitingForEmpty = false; + private boolean hasReported = false; + private BukkitRunnable waitTask = null; + + public SalesStatsManager(Main plugin) { + this.plugin = plugin; + startMonitor(); + } + + // ★ 每10秒检测一次 + private void startMonitor() { + new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 有玩家在线 → 重置等待状态 + if (waitingForEmpty) { + // 刚有人进来了,清除已汇报标记 + // 下次没人时会重新汇报 + } + waitingForEmpty = false; + hasReported = false; + cancelWaitTask(); + } else if (!waitingForEmpty && !hasReported) { + // 没人了,且还没汇报过 → 开始1分钟倒计时 + waitingForEmpty = true; + startWaitCountdown(); + } + } + }.runTaskTimer(plugin, 200L, 200L); + } + + // ★ 1分钟等待 + private void startWaitCountdown() { + cancelWaitTask(); + waitTask = new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 等待期间有人进来 → 取消,等下次没人 + waitingForEmpty = false; + hasReported = false; + waitTask = null; + cancel(); + return; + } + + // 1分钟没人 → 汇报一次 + if (!hasReported) { + hasReported = true; + report(); + } + waitingForEmpty = false; + waitTask = null; + cancel(); + } + }; + waitTask.runTaskLater(plugin, 1200L); // 60秒 + } + + private void cancelWaitTask() { + if (waitTask != null) { + waitTask.cancel(); + waitTask = null; + } + } + + // ===== 记录 ===== + + public void recordSale(String itemName, int price) { + sessionSales.merge(itemName, 1, Integer::sum); + sessionRevenue += price; + sessionOrders++; + sessionItems++; + } + + public void recordRefund(int amount) { + sessionRefunds++; + sessionRefundAmount += amount; + } + + // ===== 汇报 ===== + + public void report() { + if (sessionItems == 0 && sessionRefunds == 0) { + // plugin.getLogger().info("[Sales] 无数据,跳过"); + return; + } + + long minutes = + (System.currentTimeMillis() - sessionStart) + / 60000; + if (minutes < 1) minutes = 1; + + StringBuilder sb = new StringBuilder(); + sb.append("§6§l========== 销量汇总 =========="); + sb.append("\n§7时长: §f").append(minutes).append("分钟"); + sb.append("\n§7订单: §f").append(sessionOrders).append("笔"); + sb.append("\n§7商品: §f").append(sessionItems).append("件"); + sb.append("\n§7成交额: §6").append(sessionRevenue).append("枚"); + sb.append("\n§7退款: §c").append(sessionRefunds) + .append("次(§c").append(sessionRefundAmount) + .append("枚§7)"); + sb.append("\n§6§l------------------------------"); + + if (!sessionSales.isEmpty()) { + sb.append("\n§7销量排行:"); + List> sorted = + new ArrayList<>(sessionSales.entrySet()); + sorted.sort((a, b) -> + b.getValue() - a.getValue()); + int rank = 1; + for (Map.Entry e : sorted) { + if (rank > 10) break; + sb.append("\n§e").append(rank).append(". ") + .append(e.getKey()) + .append(" §7x").append(e.getValue()); + rank++; + } + } + sb.append("\n§6§l=============================="); + + String msg = sb.toString(); + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.isOp()) { + for (String line : msg.split("\n")) + p.sendMessage(line); + } + } + plugin.getLogger().info("[Sales] 汇报完成"); + resetSession(); + } + + public void resetSession() { + sessionSales.clear(); + sessionRevenue = 0; + sessionOrders = 0; + sessionItems = 0; + sessionRefunds = 0; + sessionRefundAmount = 0; + sessionStart = System.currentTimeMillis(); + } +} diff --git a/backup/src/main/java/Sdf1_login/ShopCategory.java b/backup/src/main/java/Sdf1_login/ShopCategory.java new file mode 100644 index 0000000..715e9ad --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ShopCategory.java @@ -0,0 +1,32 @@ +package Sdf1_login; + +import java.util.ArrayList; +import java.util.List; + +public class ShopCategory { + private final String name; + private final String fileName; + private final List items; + + public ShopCategory(String name, String fileName) { + this.name = name; + this.fileName = fileName; + this.items = new ArrayList<>(); + } + + public String getName() { return name; } + public String getFileName() { return fileName; } + public List getItems() { return items; } + + public ShopItem getItem(String id) { + for (ShopItem item : items) { + if (item.getId().equalsIgnoreCase(id)) + return item; + } + return null; + } + + public void addItem(ShopItem item) { + items.add(item); + } +} diff --git a/backup/src/main/java/Sdf1_login/ShopItem.java b/backup/src/main/java/Sdf1_login/ShopItem.java new file mode 100644 index 0000000..d336c7c --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ShopItem.java @@ -0,0 +1,49 @@ +package Sdf1_login; + +import org.bukkit.Material; + +public class ShopItem { + private final String id; + private final String displayName; + private final Material material; + private final int buyPrice; + private final int sellPrice; + private int stock; + private int hourlySales; + private int totalSales; + public ShopItem(String id, String displayName, + Material material, int buyPrice, + int sellPrice, int stock, + int hourlySales, int totalSales) { + this.id = id; + this.displayName = displayName; + this.material = material; + this.buyPrice = buyPrice; + this.sellPrice = sellPrice; + this.stock = stock; + this.hourlySales = hourlySales; + this.totalSales = totalSales; + } + + public int getEffectiveSellPrice() { + if (sellPrice <= 0) return 0; + return sellPrice; + } + + public String getId() { return id; } + public String getDisplayName() { return displayName; } + public Material getMaterial() { return material; } + public int getBuyPrice() { return buyPrice; } + public int getSellPrice() { return sellPrice; } + public int getStock() { return stock; } + public int getHourlySales() { return hourlySales; } + public int getTotalSales() { return totalSales; } + + public void setStock(int s) { this.stock = s; } + public void setHourlySales(int s) { this.hourlySales = s; } + public void setTotalSales(int s) { this.totalSales = s; } + public void addSales(int n) { + this.hourlySales += n; + this.totalSales += n; + } +} diff --git a/backup/src/main/java/Sdf1_login/ShopManager.java b/backup/src/main/java/Sdf1_login/ShopManager.java new file mode 100644 index 0000000..cdcc045 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/ShopManager.java @@ -0,0 +1,2610 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Base64; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.inventory.meta.PotionMeta; + + + + +public class ShopManager implements Listener { + + private final Main plugin; + private final List categories = + new ArrayList<>(); + private final List refundRecords = + new ArrayList<>(); + private final Map activeDiscount = + new HashMap<>(); + private final Map viewCat = + new HashMap<>(); + private final Map viewPage = + new HashMap<>(); + private final Map> shoppingCarts = + new HashMap<>(); + private final Set cartModeEnabled = + new HashSet<>(); + private final java.util.Random rng = + new java.util.Random(); + private final Map> pendingCheckout = + new HashMap<>(); + private final Set couponListening = + new HashSet<>(); + private final Map pendingCouponCode = + new HashMap<>(); + // ===== 物品NBT数据 ===== + private final Map itemNbtData = + new HashMap<>(); + private final Map itemStackCache = + new HashMap<>(); + // 替换字段 + private final Map couponApplyTime = new HashMap<>(); + + + + private static final Material[] SHULKER_COLORS = { + Material.SHULKER_BOX, + Material.WHITE_SHULKER_BOX, + Material.ORANGE_SHULKER_BOX, + Material.MAGENTA_SHULKER_BOX, + Material.LIGHT_BLUE_SHULKER_BOX, + Material.YELLOW_SHULKER_BOX, + Material.LIME_SHULKER_BOX, + Material.PINK_SHULKER_BOX, + Material.GRAY_SHULKER_BOX, + Material.LIGHT_GRAY_SHULKER_BOX, + Material.CYAN_SHULKER_BOX, + Material.PURPLE_SHULKER_BOX, + Material.BLUE_SHULKER_BOX, + Material.BROWN_SHULKER_BOX, + Material.GREEN_SHULKER_BOX, + Material.RED_SHULKER_BOX, + Material.BLACK_SHULKER_BOX + }; + private static final Map EFFECT_MAP = new HashMap<>(); + static { + // ===== 基础效果 ===== + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("速度", PotionEffectType.SPEED); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); // 1.21+ 改名 + EFFECT_MAP.put("治疗", PotionEffectType.INSTANT_HEALTH); // 1.21+ 改名 + EFFECT_MAP.put("治愈", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("伤害", PotionEffectType.INSTANT_DAMAGE); // 1.21+ 改名 + EFFECT_MAP.put("剧毒", PotionEffectType.POISON); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("回复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("再生", PotionEffectType.REGENERATION); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性", PotionEffectType.RESISTANCE); // 1.21+ 改名 + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); // 1.21+ 改名 + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); // 1.21+ 改名 + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); // ★ 补充 + EFFECT_MAP.put("迟缓", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("不祥", PotionEffectType.UNLUCK); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("生命加成", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("海豚关怀", PotionEffectType.DOLPHINS_GRACE); + EFFECT_MAP.put("跳跃", PotionEffectType.JUMP_BOOST); + // ★ 神龟药水是双效果,特殊处理,这里只做名称匹配入口 + EFFECT_MAP.put("神龟", PotionEffectType.RESISTANCE); + } + + public static class CartItem { + String itemId; + String displayName; + Material material; + int unitPrice; + int quantity; + } + + + private static final int PAGE_SIZE = 28; + private static final int[] ITEM_SLOTS = { + 10,11,12,13,14,15,16, + 19,20,21,22,23,24,25, + 28,29,30,31,32,33,34, + 37,38,39,40,41,42,43 + }; + + public ShopManager(Main plugin) { + this.plugin = plugin; + loadCategories(); + startHourlyTask(); + loadItemNbtData(); + startCleanupTask(); + } + // ===== 切换购物车模式 ===== + // ===== 切换购物车模式 ===== + public boolean toggleCartMode(Player p) { + UUID uuid = p.getUniqueId(); + if (cartModeEnabled.contains(uuid)) { + cartModeEnabled.remove(uuid); + p.sendMessage("§7购物车模式已关闭,恢复正常购买"); + return false; + } else { + cartModeEnabled.add(uuid); + p.sendMessage("§a购物车模式已开启,点击商品加入购物车"); + return true; + } + } + + public boolean isCartMode(Player p) { + return cartModeEnabled.contains(p.getUniqueId()); + } + private String itemToBase64(ItemStack item) { + try { + ByteArrayOutputStream os = + new ByteArrayOutputStream(); + BukkitObjectOutputStream bos = + new BukkitObjectOutputStream(os); + bos.writeObject(item); + bos.close(); + return Base64.getEncoder() + .encodeToString(os.toByteArray()); + } catch (Exception e) { + return ""; + } + } + + private ItemStack base64ToItem(String b64) { + try { + byte[] data = + Base64.getDecoder().decode(b64); + ByteArrayInputStream is = + new ByteArrayInputStream(data); + BukkitObjectInputStream bis = + new BukkitObjectInputStream(is); + ItemStack item = + (ItemStack) bis.readObject(); + bis.close(); + return item; + } catch (Exception e) { + return null; + } + } + + private void loadItemNbtData() { + itemNbtData.clear(); + itemStackCache.clear(); + File f = new File(plugin.getDataFolder(), + "shop/item_nbt.properties"); + if (!f.exists()) return; + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String id = + line.substring(0, eq).trim(); + String b64 = + line.substring(eq + 1).trim(); + itemNbtData.put(id, b64); + ItemStack stack = base64ToItem(b64); + if (stack != null) { + itemStackCache.put(id, stack); + } + } + br.close(); + // plugin.getLogger().info("[Shop] NBT数据: " + // + itemStackCache.size() + "条"); + } catch (Exception ignored) {} + } + + private void saveItemNbtData() { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, "item_nbt.properties"); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 商品完整数据(自动生成)"); + for (Map.Entry e + : itemNbtData.entrySet()) { + pw.println(e.getKey() + "=" + + e.getValue()); + } + pw.flush(); + pw.close(); + } catch (Exception ignored) {} + } + + // ===== 加入/移除购物车 ===== + public boolean addToCart(Player p, ShopItem item) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts + .computeIfAbsent( + uuid, k -> new ArrayList<>()); + // ★ 始终用原价,折扣在结算时统一应用 + int price = item.getBuyPrice(); + for (CartItem ci : cart) { + if (ci.itemId.equals(item.getId())) { + ci.quantity += 1; + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + } + CartItem ci = new CartItem(); + ci.itemId = item.getId(); + ci.displayName = item.getDisplayName(); + ci.material = item.getMaterial(); + ci.unitPrice = price; + ci.quantity = 1; + cart.add(ci); + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + + + public int getCartCount(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int c = 0; + for (CartItem ci : cart) c += ci.quantity; + return c; + } + + public int getCartTotal(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int t = 0; + for (CartItem ci : cart) + t += ci.unitPrice * ci.quantity; + return t; + } + + public void cartAddOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + cart.get(index).quantity += 1; + } + + public void cartRemoveOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + CartItem ci = cart.get(index); + ci.quantity -= 1; + if (ci.quantity <= 0) cart.remove(index); + } + + public void clearCart(Player p) { + shoppingCarts.remove(p.getUniqueId()); + } + + // ===== 打开购物车页面 ===== + public void openCart(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§a§l购物车"); + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + for (int i = 0; i < cart.size() && i < 25; i++) { + CartItem ci = cart.get(i); + ItemStack is = new ItemStack(ci.material); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + ci.displayName + + " §7x" + ci.quantity); + im.setLore(java.util.Arrays.asList( + "§7单价: §f" + ci.unitPrice + "枚", + "§7小计: §6" + + (ci.unitPrice * ci.quantity) + + "枚", + "", + "§a左键+1 §c右键-1")); + is.setItemMeta(im); + } + g.setItem(10 + i, is); + } + if (cart.isEmpty()) { + g.setItem(22, mkItem(Material.BARRIER, + "§7购物车为空")); + } + int total = getCartTotal(p); + int bal = plugin.getBonds().getBonds(p.getName()); + g.setItem(49, mkItem(Material.EMERALD, + "§6§l结算: §e" + total + " §6枚债券", + "§7余额: §f" + bal + "枚", + bal >= total ? "§a余额充足" : "§c余额不足", + "", "§a点击结算")); + g.setItem(48, mkItem( + Material.LIME_STAINED_GLASS_PANE, + "§7清空购物车")); + g.setItem(53, mkItem(Material.ARROW, "§7返回商店")); + + p.openInventory(g); + } + + // ===== 购物车点击处理 ===== + public boolean handleCartClick(Player p, int raw, + boolean left, + boolean right) { + if (raw == 53) { openShopMain(p); return true; } + if (raw == 48) { + clearCart(p); + p.sendMessage("§7购物车已清空"); + openCart(p); + return true; + } + if (raw == 49) { + checkoutCartInit(p); + return true; + } + if (raw >= 10 && raw < 35) { + int idx = raw - 10; + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + if (idx < cart.size()) { + if (right) cartRemoveOne(p, idx); + else cartAddOne(p, idx); + openCart(p); + } + return true; + } + return true; + } + + // ===== 结算入口:判断打包方式 ===== + private void checkoutCartInit(Player p) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts.get(uuid); + if (cart == null || cart.isEmpty()) { + p.sendMessage("§c购物车为空"); + return; + } + int total = getCartTotal(p); + int bal = plugin.getBonds() + .getBonds(p.getName()); + if (bal < total) { + p.sendMessage("§c余额不足,需要 §e" + + total + "§c枚"); + return; + } + int totalQty = 0; + for (CartItem ci : cart) totalQty += ci.quantity; + if (cart.size() == 1 && totalQty <= 64) { + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + deductAndGive(p, cart, + (int) Math.round(total * factor), + false, null, "", 0, + ecoPct, couponPct); + return; + } + pendingCheckout.put(uuid, + new ArrayList<>(cart)); + openPackingConfirm(p, total); + } + + + + // ===== 执行扣款+发货 ===== + private boolean deductAndGive(Player p, + List cart, int baseTotal, + boolean useShulker, Material color, + String packNote, int packFee, + int ecoPct, int couponPct) { + + UUID uuid = p.getUniqueId(); + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + String[] names = new String[cart.size()]; + int[] subs = new int[cart.size()]; + for (int i = 0; i < cart.size(); i++) { + CartItem ci = cart.get(i); + names[i] = ci.displayName; + subs[i] = ci.unitPrice * ci.quantity; + } + + if (baseTotal < originalTotal) { + double ratio = + (double) baseTotal / originalTotal; + int sum = 0; + for (int i = 0; i < subs.length; i++) { + if (i == subs.length - 1) { + subs[i] = baseTotal - sum; + } else { + subs[i] = (int) (subs[i] * ratio); + sum += subs[i]; + } + } + } + + boolean ok = plugin.getBonds().checkoutCart( + p.getName(), names, subs); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + if (packFee > 0) { + plugin.getBonds().deductBonds( + p.getName(), packFee, + "shop_packing", "", + "商店系统", "打包费"); + } + + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + if (si != null) { + if (si.getStock() >= 0) + si.setStock(si.getStock() + - ci.quantity); + si.addSales(ci.quantity); + } + } + + int newBal = plugin.getBonds() + .getBonds(p.getName()); + + + // ===== 优惠券码 ===== + String usedCouponCode = + pendingCouponCode.get(uuid); + + // ===== 记录订单 ===== + OrderManager.OrderRecord latestOrder = null; + try { + List orderItems = + new ArrayList<>(); + for (CartItem ci : cart) { + orderItems.add( + new OrderManager.OrderItem( + ci.displayName, + ci.material.name(), + ci.unitPrice, + ci.unitPrice, + ci.quantity)); + } + String dType = "none"; + String cCode = ""; + if (usedCouponCode != null) { + dType = "coupon"; + cCode = usedCouponCode; + } else if (ecoPct > 0) { + dType = "eco"; + } + plugin.getOrderManager().recordOrder( + p, orderItems, originalTotal, + baseTotal + packFee, + originalTotal - baseTotal, + dType, cCode, packFee, + useShulker ? (packFee > 0 + ? "custom" : "default") + : "none", + color != null ? color.name() : ""); + // 获取最新订单 + synchronized (plugin.getOrderManager() + .getOrders()) { + List all = + plugin.getOrderManager() + .getOrders(); + if (!all.isEmpty()) + latestOrder = + all.get(all.size() - 1); + } + // 散装打印小票 + if (!useShulker && latestOrder != null) { + plugin.getOrderManager() + .sendReceiptChat(p, latestOrder); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + // + ex.getMessage()); + } + +// ===== 发货 ===== + if (!useShulker) { + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + ItemStack give = (si != null) + ? getShopStack(si, ci.quantity) + : new ItemStack(ci.material, ci.quantity); + HashMap left2 = + p.getInventory().addItem(give); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } else { + List shulkers = + packShulkers(cart, color); + + // ★ 给潜影盒打订单ID标签 + if (latestOrder != null) { + for (ItemStack sh : shulkers) { + ItemMeta shm = sh.getItemMeta(); + if (shm != null) { + List lore = shm.getLore() != null + ? new ArrayList<>(shm.getLore()) + : new ArrayList<>(); + lore.add("§8#" + latestOrder.orderId); + shm.setLore(lore); + sh.setItemMeta(shm); + } + } + } + + // 小票书塞入 + if (!shulkers.isEmpty() + && latestOrder != null) { + ItemStack book = plugin.getOrderManager() + .createReceiptBook(latestOrder); + plugin.getOrderManager() + .addBookToShulker(shulkers.get(0), book); + } + // 发货 + for (ItemStack sh : shulkers) { + HashMap left2 = + p.getInventory().addItem(sh); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } + + + // ===== 退款记录 ===== + long now = System.currentTimeMillis(); + if (!useShulker) { + for (CartItem ci : cart) { + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + ci.itemId, ci.displayName, + ci.material, null, + ci.quantity, + ci.unitPrice * ci.quantity, + now)); + } + } else { + String summary = ""; + for (CartItem ci : cart) { + if (!summary.isEmpty()) summary += "+"; + summary += ci.displayName + + "x" + ci.quantity; + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + "cart_" + now, summary, + Material.SHULKER_BOX, color, + 1, baseTotal, now)); + } + + // ===== 核销优惠券 ===== + if (usedCouponCode != null) { + activeDiscount.remove(uuid); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCouponCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCouponCode + + " §7已核销"); + } + + shoppingCarts.remove(uuid); + pendingCheckout.remove(uuid); + return true; + } + + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + markShulkerUsed(item); + } + + // ★ 公共方法:标记潜影盒为已使用 + private void markShulkerUsed(ItemStack item) { + if (item == null) return; + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + // shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + @EventHandler + public void onPlayerDrop(org.bukkit.event.player.PlayerDropItemEvent e) { + ItemStack item = e.getItemDrop().getItemStack(); + if (item != null && item.getType().name().contains("SHULKER")) { + markShulkerUsed(item); + } + } + + + // ===== 潠影盒打包(指定颜色)===== + private List packShulkers( + List items, Material color) { + List result = new ArrayList<>(); + List all = new ArrayList<>(); + for (CartItem ci : items) { + int rem = ci.quantity; + while (rem > 0) { + int sz = Math.min(rem, 64); + all.add(new ItemStack(ci.material, sz)); + rem -= sz; + } + } + while (!all.isEmpty()) { + ItemStack shulker = new ItemStack(color); + org.bukkit.inventory.meta.BlockStateMeta meta = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (meta != null) { + org.bukkit.block.BlockState state = + meta.getBlockState(); + if (state instanceof org.bukkit.block.Container) { + org.bukkit.block.Container c = + (org.bukkit.block.Container) state; + List remain = new ArrayList<>(); + for (ItemStack stack : all) { + HashMap left = + c.getInventory().addItem(stack); + for (ItemStack l : left.values()) + remain.add(l); + } + all = remain; + meta.setBlockState(c); + shulker.setItemMeta(meta); + } + } + result.add(shulker); + } + + return result; + } + + // ===== 打包确认GUI ===== + public void openPackingConfirm(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l选择包装方式"); + g.setItem(10, mkItem(Material.CHEST, + "§a§l不打包", + "§7直接放入背包", + "§7适合少量物品")); + g.setItem(13, mkItem(Material.SHULKER_BOX, + "§6§l默认打包", + "§7随机颜色潜影盒", + "§7加收 §e5 §6枚债券")); + g.setItem(16, mkItem(Material.ENDER_CHEST, + "§b§l自选颜色", + "§7选择你喜欢的潜影盒颜色", + "§7加收 §e5 §6枚债券")); + g.setItem(22, mkItem(Material.PAPER, + "§7消费金额: §e" + baseTotal + "枚")); + + p.openInventory(g); + } + + public boolean handlePackingConfirm( + Player p, int raw) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + + if (raw == 10) { + ecoPct = 2; + } + + double factor = 1.0; + if (ecoPct > 0) + factor *= (100 - ecoPct) / 100.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + originalTotal * factor); + + int totalQty = 0; + for (CartItem ci : cart) + totalQty += ci.quantity; + boolean multiAndBig = (cart.size() > 1 + && originalTotal >= 100); + + if (raw == 10) { + deductAndGive(p, cart, discounted, + false, null, "", 0, + ecoPct, couponPct); + return true; + } + if (raw == 13) { + Material c = Material.SHULKER_BOX; + if (multiAndBig) { + deductAndGive(p, cart, discounted, + true, c, "", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, + true, c, "默认打包", 5, + ecoPct, couponPct); + } + return true; + } + if (raw == 16) { + pendingCheckout.put(uuid, cart); + openColorSelect(p, discounted); + return true; + } + return true; + } + + + // ===== 自选颜色GUI ===== + public void openColorSelect(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 54, "§b§l选择潜影盒颜色"); + String[] colorNames = { + "白色","橙色","品红","淡蓝", + "黄色","淡绿","粉色","灰色", + "淡灰","青色","紫色","蓝色", + "棕色","绿色","红色","黑色","原色" + }; + for (int i = 0; i < SHULKER_COLORS.length; i++) { + ItemStack is = new ItemStack(SHULKER_COLORS[i]); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + colorNames[i] + + "§6潜影盒"); + im.setLore(java.util.Arrays.asList( + "§7加收 §e5 §6枚债券", + "", + "§a点击选择")); + is.setItemMeta(im); + } + g.setItem(i, is); + } + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(45, mkItem(Material.PAPER, + "§7消费: §e" + baseTotal + + "枚 + §e5§6打包费")); + + p.openInventory(g); + } + + public boolean handleColorSelect( + Player p, int raw) { + if (raw == 49) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.get(uuid); + if (cart != null) { + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + openPackingConfirm(p, total); + } + return true; + } + if (raw < 0 || raw >= SHULKER_COLORS.length) + return false; + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + total * factor); + + Material selectedColor = SHULKER_COLORS[raw]; + if (selectedColor == Material.SHULKER_BOX) { + deductAndGive(p, cart, discounted, true, + Material.SHULKER_BOX, + "原色打包", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, true, + selectedColor, "自选颜色", 5, + ecoPct, couponPct); + } + return true; + } + + + + // ===== 加载分类 ===== + + public void loadCategories() { + // plugin.getLogger().info("[Shop] === loadCategories 被调用 ==="); // 过滤调试日志 + categories.clear(); + viewCat.clear(); // ★ 清缓存 + viewPage.clear(); + + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + + convertTxtToMd(dir); + + File[] allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + if (allMd == null || allMd.length == 0) { + createDefaultCategories(dir); + allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + } + if (allMd == null) { + // plugin.getLogger().warning( + // "[Shop] shop目录为空"); + return; + } + + /* plugin.getLogger().info("[Shop] === 开始加载 ==="); + plugin.getLogger().info("[Shop] 扫描到 " + + allMd.length + " 个md文件"); +*/ + for (File f : allMd) { + /* plugin.getLogger().info("[Shop] 读取: " + + f.getName());*/ + ShopCategory cat = parseFile(f); + if (cat != null) { + /* plugin.getLogger().info("[Shop] 分类: " + + cat.getName() + + " 商品数: " + + cat.getItems().size());*/ + if (!cat.getItems().isEmpty()) { + categories.add(cat); + } + } + } + + int totalItems = 0; + for (ShopCategory c : categories) + totalItems += c.getItems().size(); + + plugin.getLogger().info("[Shop] === 加载完成 ===" + + " 分类=" + categories.size() + + " 商品=" + totalItems); + } + + +// ===== txt → md 自动迁移 ===== + + private void convertTxtToMd(File dir) { + File[] txtFiles = dir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("coupons.txt")); + if (txtFiles == null) return; + for (File txt : txtFiles) { + String mdName = txt.getName() + .replace(".txt", ".md"); + File mdFile = new File(dir, mdName); + if (mdFile.exists()) continue; + + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(txt), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) + lines.add(line); + br.close(); + + String catName = txt.getName() + .replace(".txt", ""); + List out = new ArrayList<>(); + out.add("# " + catName); + out.add(""); + out.add("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + out.add("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + + for (String l : lines) { + l = l.trim(); + if (l.isEmpty() || l.startsWith("#")) + continue; + // 旧格式: ID | 名称 | 材质 | ... + // 保持原样转换为 md 表格行 + out.add("| " + l.replace("|", "|") + + " |"); + } + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(mdFile), + StandardCharsets.UTF_8)); + for (String o : out) + pw.println(o); + pw.flush(); + pw.close(); + + txt.delete(); + // plugin.getLogger().info( + // "[Shop] 已迁移: " + // + txt.getName() + // + " → " + mdName); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 迁移失败: " + // + txt.getName()); + } + } + } + +// ===== 解析 md 文件 ===== + + private ShopCategory parseFile(File f) { + String name = f.getName() + .replace(".md", ""); + ShopCategory cat = + new ShopCategory(name, f.getName()); + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean headerPassed = false; + boolean separatorPassed = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + + // 空行跳过 + if (line.isEmpty()) continue; + + // 标题行:# 分类名 + if (line.startsWith("#")) { + String t = line.replaceFirst( + "^#+\\s*", "").trim(); + if (!t.isEmpty()) { + // 更新分类名(用文件内标题) + // cat.name 是 final,不改 + } + continue; + } + + // 表格分隔行:| --- | --- | + if (line.contains("---")) { + separatorPassed = true; + continue; + } + + // 表头行:| ID | 品名 | ... + if (line.startsWith("|")) { + if (!separatorPassed) { + headerPassed = true; + continue; + } + // 数据行 + ShopItem item = + parseTableRow(line); + if (item != null) + cat.addItem(item); + } + } + br.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 解析失败: " + f.getName() + // + " 原因: " + e.getMessage()); + return null; + } + return cat; + } + +// ===== 解析表格行 ===== + + private ShopItem parseTableRow(String row) { + // plugin.getLogger().info("[Shop] 行: " + row); // 过滤调试日志 + + String trimmed = row.trim(); + if (trimmed.startsWith("|")) + trimmed = trimmed.substring(1); + if (trimmed.endsWith("|")) + trimmed = trimmed.substring(0, + trimmed.length() - 1); + + String[] p = trimmed.split("\\|"); + // plugin.getLogger().info("[Shop] 列数=" + p.length); // 过滤调试日志 + + if (p.length < 6) { + // plugin.getLogger().warning( + // "[Shop] ✗ 列数不足, 跳过"); + return null; + } + + String rawId = p[0].trim(); + String rawName = p[1].trim(); + String rawMat = p[2].trim().toUpperCase(); + String rawBuy = p[3].trim(); + String rawSell = p[4].trim(); + String rawStock = p[5].trim(); + + // plugin.getLogger().info("[Shop] ID=" + // + rawId + " 名=" + rawName + // + " 材质=" + rawMat + // + " 购=" + rawBuy + " 售=" + rawSell + // + " 库=" + rawStock); // 过滤调试日志 + + // 检查材质是否存在 + Material mat = Material.matchMaterial(rawMat); + if (mat == null) { + // plugin.getLogger().warning( + // "[Shop] ✗ 无效材质: " + rawMat + // + " (行: " + row + ")"); + return null; + } + + try { + ShopItem item = new ShopItem( + rawId, rawName, mat, + Integer.parseInt(rawBuy), + Integer.parseInt(rawSell), + Integer.parseInt(rawStock), + p.length > 6 + ? Integer.parseInt( + p[6].trim()) : 0, + p.length > 7 + ? Integer.parseInt( + p[7].trim()) : 0); + // plugin.getLogger().info("[Shop] ✓ 解析成功: " + rawId); // 过滤调试日志 + return item; + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] ✗ 数字解析失败: " + // + e.getMessage()); + return null; + } + } + + +// ===== 保存为 md ===== + + public void saveCategory(ShopCategory cat) { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, cat.getFileName()); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# " + cat.getName()); + pw.println(""); + pw.println("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + pw.println("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + for (ShopItem item : cat.getItems()) { + pw.println("| " + + item.getId() + + " | " + item.getDisplayName() + + " | " + item.getMaterial().name() + + " | " + item.getBuyPrice() + + " | " + item.getSellPrice() + + " | " + item.getStock() + + " | " + item.getHourlySales() + + " | " + item.getTotalSales() + + " |"); + } + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 保存失败: " + // + cat.getFileName()); + } + } + +// ===== 创建示例分类 ===== + + private void createDefaultCategories(File dir) { + writeMd(dir, "建筑材料.md", + "# 建筑材料\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| STONE | 石头 | STONE" + + " | 10 | -1 | -1 | 0 | 0 |\n" + + "| COBBLESTONE | 圆石 | COBBLESTONE" + + " | 5 | -1 | -1 | 0 | 0 |\n" + + "| OAK_PLANKS | 橡木木板 | OAK_PLANKS" + + " | 8 | 3 | -1 | 0 | 0 |\n" + + "| BRICKS | 砖块 | BRICKS" + + " | 12 | -1 | -1 | 0 | 0 |\n" + + "| GLASS | 玻璃 | GLASS" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| QUARTZ_BLOCK | 石英块 | QUARTZ_BLOCK" + + " | 20 | 8 | -1 | 0 | 0 |\n"); + + writeMd(dir, "装备工具.md", + "# 装备工具\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| IRON_PICKAXE | 铁镐 | IRON_PICKAXE" + + " | 50 | 20 | -1 | 0 | 0 |\n" + + "| DIAMOND_PICKAXE | 钻石镐 | DIAMOND_PICKAXE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| IRON_SWORD | 铁剑 | IRON_SWORD" + + " | 40 | 16 | -1 | 0 | 0 |\n" + + "| DIAMOND_SWORD | 钻石剑 | DIAMOND_SWORD" + + " | 400 | 160 | -1 | 0 | 0 |\n" + + "| ELYTRA | 鞘翅 | ELYTRA" + + " | 2000 | 800 | -1 | 0 | 0 |\n"); + + writeMd(dir, "食物补给.md", + "# 食物补给\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| COOKED_BEEF | 熟牛排 | COOKED_BEEF" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| GOLDEN_APPLE | 金苹果 | GOLDEN_APPLE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| BREAD | 面包 | BREAD" + + " | 8 | 3 | -1 | 0 | 0 |\n"); + + // plugin.getLogger().info( + // "[Shop] 已创建3个示例分类(md)"); + } + + private void writeMd(File dir, String name, + String content) { + File f = new File(dir, name); + if (f.exists()) return; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.print(content); + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 创建失败: " + name); + } + } + + /** + * 从商品名称+材质智能推断完整物品 + * + * "夜视药水" + POTION + * → PotionEffect(NIGHT_VISION, 600, 0) + * + * "喷溅型治疗药水" + SPLASH_POTION + * → PotionEffect(HEALING, 600, 0) + * + * "长效夜视之箭" + TIPPED_ARROW + * → PotionEffect(NIGHT_VISION, 1800, 0) + * + * "强效力量喷溅药水" + SPLASH_POTION + * → PotionEffect(STRENGTH, 600, 1) + */ + private ItemStack inferFromName( + String name, Material mat) { + // 只处理药水和箭 + if (mat != Material.POTION + && mat != Material.SPLASH_POTION + && mat != Material.LINGERING_POTION + && mat != Material.TIPPED_ARROW) { + return null; + } + + // 1. 找效果类型 + PotionEffectType effectType = null; + for (Map.Entry e + : EFFECT_MAP.entrySet()) { + if (name.contains(e.getKey())) { + effectType = e.getValue(); + break; + } + } + if (effectType == null) return null; + + // 2. 找时长 + int duration = 600; + if (name.contains("长效") || name.contains("长时") + || name.contains("持久") || name.contains("长命")) { + duration = 1800; + } +// ★ 瞬间效果药水:duration = 1 + if (name.contains("治疗") || name.contains("治愈") + || name.contains("伤害")) { + duration = 1; + } + + // 3. 找等级 + int amplifier = 0; + if (name.contains("强效") + || name.contains("强化") + || name.contains("加强")) { + amplifier = 1; + } + + // 4. 构造完整物品 + try { + ItemStack item = new ItemStack(mat); + PotionMeta meta = + (PotionMeta) item.getItemMeta(); + + // ★ 神龟药水:双效果 + if (name.contains("神龟")) { + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.RESISTANCE, + duration, amplifier + 2), + true); + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.SLOWNESS, + duration, amplifier + 3), + true); + } else { + // 普通药水:单效果 + meta.addCustomEffect( + new PotionEffect(effectType, + duration, amplifier), + true); + } + + item.setItemMeta(meta); + return item; + } catch (Exception e) { + return null; + } + } + + public void saveAll() { + for (ShopCategory cat : categories) + saveCategory(cat); + } + private ItemStack getShopStack( + ShopItem item, int amount) { + // 优先级1: 手动捕获的NBT数据 + ItemStack cached = + itemStackCache.get(item.getId()); + if (cached != null) { + ItemStack copy = cached.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级2: 根据名称智能推断 + ItemStack inferred = inferFromName( + item.getDisplayName(), + item.getMaterial()); + if (inferred != null) { + ItemStack copy = inferred.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级3: 纯材质(默认) + return new ItemStack( + item.getMaterial(), amount); + } + + // ===== 购买 ===== + + public boolean buyItem(Player p, ShopItem item, + int amount) { + // plugin.getLogger().info("[Shop] ★ buyItem item=" + (item != null ? item.getId() : "null") + " amount=" + amount); + // plugin.getLogger().info("buyItem被调用"); + if (item == null) return false; + if (item.getStock() >= 0 + && item.getStock() < amount) { + p.sendMessage("§c库存不足,剩余: " + + item.getStock()); + return false; + } + if (p.getInventory().firstEmpty() == -1 + && !p.getInventory().contains( + item.getMaterial(), 1)) { + p.sendMessage("§c背包已满"); + return false; + } + + int unit = item.getBuyPrice(); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int total = Math.max(0, + unit * amount * (100 - disc) / 100); + + int balance = plugin.getBonds() + .getBonds(p.getName()); + if (balance < total) { + p.sendMessage("§c债券不足,需要 §e" + + total + "§c枚,当前 §e" + + balance + "§c枚"); + return false; + } + + boolean ok = plugin.getBonds().deductBonds( + p.getName(), total, + "shop_buy", item.getId(), + "商店系统", "购买" + + item.getDisplayName()); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + ItemStack give = getShopStack(item, amount); + HashMap left = + p.getInventory().addItem(give); + for (ItemStack drop : left.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + + if (item.getStock() >= 0) + item.setStock( + item.getStock() - amount); + item.addSales(amount); + + mergeOrAddRefund(p, item, amount, total); + Long applyTime = couponApplyTime.get(p.getUniqueId()); + if (applyTime != null + && System.currentTimeMillis() - applyTime > 300000L) { + // 超过5分钟,自动失效 + pendingCouponCode.remove(p.getUniqueId()); + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + p.sendMessage("§7优惠券已过期,请重新输入"); + disc = 0; // 重置折扣 + } + + + String discMsg = disc > 0 + ? " §7(优惠" + disc + "%)" : ""; + p.sendMessage("§a购买成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a-" + total + "枚债券" + + discMsg); + if (disc > 0) + activeDiscount.remove( + p.getUniqueId()); + // ★ 直购成功后核销优惠券 + String usedCode = pendingCouponCode + .remove(p.getUniqueId()); + if (usedCode != null) { + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCode + + " §7已核销"); + couponApplyTime.remove(p.getUniqueId()); + } + // ★ 记录订单(包裹try-catch,失败不影响购买) + try { + String usedCouponCode = + pendingCouponCode.get(p.getUniqueId()); + List orderItems = + new ArrayList<>(); + orderItems.add(new OrderManager.OrderItem( + item.getDisplayName(), + item.getMaterial().name(), + item.getBuyPrice(), + item.getBuyPrice(), amount)); + plugin.getOrderManager().recordOrder( + p, orderItems, + item.getBuyPrice() * amount, total, + item.getBuyPrice() * amount - total, + usedCouponCode != null ? "coupon" : "none", + usedCouponCode != null ? usedCouponCode : "", + 0, "none", ""); + // 打印小票 + List all = + plugin.getOrderManager().getOrders(); + if (!all.isEmpty()) { + plugin.getOrderManager().sendReceiptChat( + p, all.get(all.size() - 1)); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + ex.getMessage()); + } + + return true; + } + + + public void handleRefundItemClick(Player p, int raw) { + if (raw < 10 || raw >= 45) return; + int target = raw - 10; + int cnt = 0; + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { it.remove(); continue; } + if (cnt == target) { + requestRefund(p, r); + return; + } + cnt++; + } + } + } + + + // ===== 出售 ===== + + public boolean sellItem(Player p, ShopItem item, + int amount) { + if (item == null) return false; + + // ★ 先判断售价,禁止出售的不收物品 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return false; + } + + if (!p.getInventory().containsAtLeast( + getShopStack(item, 1), amount)) { + p.sendMessage("§c你没有足够的 §e" + + item.getDisplayName()); + return false; + } + + int total = item.getEffectiveSellPrice() * amount; + + p.getInventory().removeItem( + getShopStack(item, amount)); + plugin.getBonds().addBonds( + p.getName(), total, "shop_sell", + item.getId(), "商店系统", + "出售" + item.getDisplayName()); + if (item.getStock() >= 0) + item.setStock(item.getStock() + amount); + p.sendMessage("§a出售成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a+" + total + "枚债券"); + return true; + } + + // ===== 退款合并:30秒内同商品合并为一个订单 ===== + private boolean mergeOrAddRefund(Player p, ShopItem item, + int amount, int total) { + long now = System.currentTimeMillis(); + synchronized (refundRecords) { + for (int i = refundRecords.size() - 1; i >= 0; i--) { + RefundRecord r = refundRecords.get(i); + boolean same = r.playerUUID.equals(p.getUniqueId()) + && r.itemId.equals(item.getId()) + && (now - r.timestamp) < 30000L; + if (same) { + // final 字段不能改,只能删旧建新 + int newAmount = r.amount + amount; + int newTotal = r.totalPaid + total; + refundRecords.remove(i); + refundRecords.add(new RefundRecord( + r.playerUUID, r.playerName, + r.itemId, r.itemName, + r.material, newAmount, + newTotal, now)); + return true; + } + } + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + item.getId(), item.getDisplayName(), + item.getMaterial(), amount, total, now)); + return false; + } + + + + // ===== 退款 ===== + + public boolean requestRefund(Player p, RefundRecord rec) { + if (rec == null) return false; + if (!rec.playerUUID.equals(p.getUniqueId())) { + p.sendMessage("§c这不是你的购买记录"); + return false; + } + if (rec.isExpired()) { + p.sendMessage("§c已超过5分钟退款期限"); + synchronized (refundRecords) { + refundRecords.remove(rec); + } + return false; + } + + // 检测物品/潜影盒是否还在 + synchronized (refundRecords) { + if (rec.shulkerMaterial != null) { + int boxCount = countShulkers(p, rec.shulkerMaterial); + if (boxCount < 1) { + p.sendMessage("§c潜影盒已移出背包,无法退款"); + refundRecords.remove(rec); + return false; + } + } else { + int have = countDirect(p, rec.material); + if (have < rec.amount) { + p.sendMessage("§c物品已转移或消耗,无法退款"); + refundRecords.remove(rec); + return false; + } + } + } + + // 退款:扣物品/潜影盒 + if (rec.shulkerMaterial != null) { + // 盒装:扣一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == rec.shulkerMaterial) { + p.getInventory().clear(i); + break; + } + } + } else { + // 普通:扣商品 + p.getInventory().removeItem( + new ItemStack(rec.material, rec.amount)); + } + + // 退款:加债券 + plugin.getBonds().addBonds( + p.getName(), rec.totalPaid, + "refund", rec.itemId, + "商店系统", "退款" + rec.itemName); + + // 恢复库存 + ShopItem si = findItemById(rec.itemId); + if (si != null && si.getStock() >= 0) + si.setStock(si.getStock() + rec.amount); + + synchronized (refundRecords) { + refundRecords.remove(rec); + } + + p.sendMessage("§a退款成功: §e" + rec.itemName + + " §a+" + rec.totalPaid + "枚债券"); + return true; + } + + + + + public void setPlayerDiscount(Player p, + int pct) { + activeDiscount.put( + p.getUniqueId(), pct); + p.sendMessage("§a已设置 " + pct + + "% 折扣,下次购买生效"); + } + + // ===== 管理员操作 ===== + + public boolean addItemToCategory( + String catName, String id, + String displayName, Material mat, + int buyPrice, int sellPrice, + int stock) { + ShopCategory cat = + findCategory(catName); + if (cat == null) return false; + if (cat.getItem(id) != null) return false; + cat.addItem(new ShopItem(id, displayName, + mat, buyPrice, sellPrice, + stock, 0, 0)); + saveCategory(cat); + return true; + } + + public boolean removeItem(String id) { + for (ShopCategory cat : categories) { + ShopItem item = cat.getItem(id); + if (item != null) { + cat.getItems().remove(item); + saveCategory(cat); + return true; + } + } + return false; + } + + public boolean setStock(String id, int stock) { + ShopItem item = findItemById(id); + if (item == null) return false; + item.setStock(stock); + for (ShopCategory cat : categories) { + if (cat.getItem(id) != null) { + saveCategory(cat); + return true; + } + } + return false; + } + + public ShopCategory findCategory(String n) { + for (ShopCategory c : categories) + if (c.getName().equals(n)) return c; + return null; + } + + public ShopItem findItemById(String id) { + for (ShopCategory c : categories) { + ShopItem item = c.getItem(id); + if (item != null) return item; + } + return null; + } + + public List getCategories() { + return categories; + } + + // ===== 命令处理 ===== + + public boolean handleCommand( + CommandSender s, String[] a) { + if (a.length <= 1) { + if (s instanceof Player) { + openShopMain((Player) s); + } else { + s.sendMessage("§c仅玩家可使用"); + } + return true; + } + + String sub = a[1].toLowerCase(); + + if (sub.equals("reload")) { + loadCategories(); + s.sendMessage("§a商店已重载,共 " + + categories.size() + + " 个分类"); + return true; + } + + if (sub.equals("add")) { + if (a.length < 7) { + s.sendMessage( + "§e用法: shop add <分类>" + + " <名称> <材质>" + + " <购入价>" + + " [售出价-1自动]" + + " [库存-1无限]"); + return true; + } + Material mat; + try { + mat = Material.valueOf( + a[5].toUpperCase()); + } catch (Exception e) { + s.sendMessage( + "§c无效材质: " + a[5]); + return true; + } + int price = Integer.parseInt(a[6]); + int sell = a.length > 7 + ? Integer.parseInt(a[7]) : -1; + int stock = a.length > 8 + ? Integer.parseInt(a[8]) : -1; + if (addItemToCategory(a[2], a[3], + a[4], mat, price, sell, stock)) { + s.sendMessage("§a已添加: " + + a[4] + " → " + a[2]); + } else { + s.sendMessage("§c添加失败" + + "(分类不存在或ID重复)"); + } + return true; + } + + if (sub.equals("remove")) { + if (a.length < 3) { + s.sendMessage( + "§e用法: shop remove "); + return true; + } + if (removeItem(a[2])) { + s.sendMessage( + "§a已移除: " + a[2]); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("set")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop set " + + " <库存数(-1无限)>"); + return true; + } + int stock = Integer.parseInt(a[3]); + if (setStock(a[2], stock)) { + s.sendMessage("§a已设置 " + + a[2] + " 库存为 " + + stock); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("pro")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop pro" + + " <玩家名> <折扣%>"); + return true; + } + Player t = + Bukkit.getPlayerExact(a[2]); + if (t == null) { + s.sendMessage("§c玩家不在线"); + return true; + } + int pct = Integer.parseInt(a[3]); + setPlayerDiscount(t, pct); + s.sendMessage("§a已对 " + a[2] + + " 设置 " + pct + "% 折扣"); + return true; + } + + + s.sendMessage("§e商店子命令:" + + " reload / add / remove" + + " / set / pro "); + return true; + } + @EventHandler(priority = EventPriority.LOW) + public void onCouponChat( + org.bukkit.event.player.AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + if (!couponListening.contains(uuid)) return; + e.setCancelled(true); + couponListening.remove(uuid); + String code = e.getMessage().trim(); + if (code.isEmpty()) { + p.sendMessage("§c已取消输入"); + return; + } + // ★ 预处理:标准化输入 + String normalized = normalizeCouponInput(code); + if (normalized.isEmpty()) { + p.sendMessage("§c优惠券码无效(输入为空或仅含特殊字符)"); + couponListening.remove(uuid); + return; + } + + p.sendMessage("§7正在验证优惠券: §e" + code); + + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "validateCoupon", + String.class); + Object result = m.invoke(sdf1, normalized); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception ex) { + p.sendMessage("§c验证异常: " + + ex.getMessage()); + return; + } + if (discount <= 0) { + p.sendMessage("§c优惠券无效或已使用"); + return; + } + int offPct = 100 - discount; + int current = activeDiscount + .getOrDefault(uuid, 0); + if (offPct > current) { + pendingCouponCode.put(uuid, normalized); + activeDiscount.put(uuid, offPct); + couponApplyTime.put(uuid, System.currentTimeMillis()); + String zhe; + if (discount % 10 == 0) + zhe = (discount / 10) + "折"; + else + zhe = (discount / 10.0) + "折"; + p.sendMessage("§6§l[优惠券] §a验证通过!"); + p.sendMessage("§7折扣: §e" + zhe + + " §7(减" + offPct + "%)"); + p.sendMessage("§7结算时自动核销"); + final UUID u = uuid; + Bukkit.getScheduler().runTask(plugin, () -> { + Player pl = Bukkit.getPlayer(u); + if (pl != null && pl.isOnline()) { + openShopMain(pl); + } + }); + } else { + String curZhe; + if ((100 - current) % 10 == 0) + curZhe = ((100 - current) / 10) + "折"; + else + curZhe = ((100 - current) / 10.0) + "折"; + p.sendMessage("§c当前已有更优折扣: " + + curZhe + ",本次不生效"); + } + } + + /** + * 优惠券输入标准化 + * 全角→半角、去分隔符、转小写 + * "AAA-BBB-CCCC" → "aaaabbbbcccc" + * "AAA-BBB" → "aaabbb" + * "aaa_bbb_cccc" → "aaabbbcccc" + */ + private String normalizeCouponInput(String s) { + if (s == null) return ""; + StringBuilder sb = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + // 全角数字→半角 + if (c >= '\uFF10' && c <= '\uFF19') { + sb.append((char)(c - 0xFF10 + '0')); + } + // 全角字母→半角 + else if (c >= '\uFF21' && c <= '\uFF3A') { + sb.append((char)(c - 0xFF21 + 'A')); + } + else if (c >= '\uFF41' && c <= '\uFF5A') { + sb.append((char)(c - 0xFF41 + 'a')); + } + // ★ 保留中文字符(Unicode CJK统一汉字范围) + else if ((c >= '\u4E00' && c <= '\u9FFF') + || (c >= '\u3400' && c <= '\u4DBF')) { + sb.append(c); + } + // 保留半角字母和数字 + else if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9')) { + sb.append(c); + } + // 分隔符(- _ 空格等)丢弃 + } + return sb.toString().toLowerCase(); + } + + + + + + private String formatZhe(int discount) { + if (discount % 10 == 0) { + return (discount / 10) + "折"; + } + return (discount / 10.0) + "折"; + } + + public boolean redeemCoupon(Player p, String code) { + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + Object result = m.invoke(sdf1, code); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception e) { + return false; + } + if (discount <= 0) return false; + int offPct = 100 - discount; + int cur = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (offPct > cur) { + activeDiscount.put(p.getUniqueId(), offPct); + } + return true; + } + + /** + * 商店主界面点击"优惠券"按钮后调用 + * 开启聊天监听,等待玩家输入优惠券码 + */ + public void openCouponInput(Player p) { + UUID uuid = p.getUniqueId(); + // 先取消已有的待核销优惠券 + pendingCouponCode.remove(uuid); + couponListening.add(uuid); + // 关闭UI + Bukkit.getScheduler().runTask(plugin, () -> { + if (p.isOnline()) p.closeInventory(); + }); + p.sendMessage("§6§l[优惠券] §e请在聊天栏输入优惠券码"); + p.sendMessage("§715秒内输入,过期自动取消"); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (couponListening.remove(uuid)) { + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§c优惠券输入已超时取消"); + } + } + }, 300L); + } + + + // ===== GUI: 主界面 ===== + + public void openShopMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l商店"); + int si = 0; + for (ShopCategory cat : categories) { + if (si >= ITEM_SLOTS.length) break; + ItemStack is = new ItemStack( + Material.CHEST); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName( + "§e§l" + cat.getName()); + im.setLore(Arrays.asList( + "§7商品: §f" + + cat.getItems().size() + + " 种", + "", + "§a点击进入")); + is.setItemMeta(im); + } + g.setItem(ITEM_SLOTS[si], is); + si++; + } + // ★ 改为跳转订单中心 + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看订单记录、申请退款", + "", "§a点击进入")); + + // 在 openShopMain 方法中,原来的 slot 49/53 附近加: + + g.setItem(48, mkItem( + isCartMode(p) + ? Material.LIME_WOOL + : Material.GRAY_WOOL, + isCartMode(p) + ? "§a§l购物车模式 §7[开]" + : "§7§l购物车模式 §7[关]", + isCartMode(p) + ? "§a当前: 点击商品加入购物车" + : "§7当前: 直接购买", + "§7点击切换")); + g.setItem(50, mkItem(Material.CHEST, + "§6§l购物车 §7[" + getCartCount(p) + "件]", + "§7总价: §6" + getCartTotal(p) + "枚", + "", + "§a点击打开购物车")); + + // 在现有按钮之后,p.openInventory(g) 之前加: + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看购物订单记录", + "", "§a点击进入")); + + // 优惠券按钮 - slot 47 + String discStatus = "§7"; + int curDisc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (curDisc > 0) { + discStatus = "§a当前: " + formatZhe(100 - curDisc); + } + g.setItem(47, mkItem(Material.NAME_TAG, + "§6§l优惠券", + discStatus, + "", + "§a点击输入优惠券码", + "§7一次性使用,核销后失效")); + + g.setItem(53, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + // ===== GUI: 分类 ===== + public void openCategory(Player p, + ShopCategory cat, + int page) { + // ★ 每次重新获取最新对象 ★ + ShopCategory fresh = null; + for (ShopCategory c : categories) { + if (c.getName().equals(cat.getName())) { + fresh = c; + break; + } + } + if (fresh == null) { + openShopMain(p); + return; + } + cat = fresh; + viewCat.put(p.getUniqueId(), cat); + + int total = (int) Math.ceil( + (double) cat.getItems().size() + / PAGE_SIZE); + if (total < 1) total = 1; + if (page < 0) page = 0; + if (page >= total) page = total - 1; + viewPage.put(p.getUniqueId(), page); + + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l" + cat.getName()); + + + int start = page * PAGE_SIZE; + int end = Math.min(start + PAGE_SIZE, + cat.getItems().size()); + + for (int i = start; i < end; i++) { + int idx = i - start; + if (idx >= ITEM_SLOTS.length) break; + + // ★ item 保留原名,代表 ShopItem 数据 + ShopItem item = cat.getItems().get(i); + + // ★ is 是带完整元数据的 ItemStack(用于GUI图标) + ItemStack is = getShopStack(item, 1); + ItemMeta im = is.getItemMeta(); + + if (im != null) { + im.setDisplayName("§e§l" + + item.getDisplayName()); + List lore = new ArrayList<>(); + lore.add("§7购入: §a" + + item.getBuyPrice() + + "§7枚债券"); + lore.add("§7售出: §e" + + item.getEffectiveSellPrice() + + "§7枚债券"); + if (item.getStock() >= 0) + lore.add("§7库存: §f" + + item.getStock()); + else + lore.add("§7库存: §a无限"); + lore.add(""); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (disc > 0) { + int dis = item.getBuyPrice() + * (100 - disc) / 100; + lore.add("§7优惠价: §6" + + dis + "枚" + + " §7(-" + disc + "%)"); + } + lore.add("§a左键买1 §6右键卖1"); + lore.add("§aShift+左键买64" + + " §6Shift+右键卖64"); + im.setLore(lore); + is.setItemMeta(im); + } + + // ★ 用 is(ItemStack)放入GUI格子 + g.setItem(ITEM_SLOTS[idx], is); + } + + + g.setItem(4, mkItem(Material.BOOK, + "§e§l" + cat.getName(), + "§7共 " + cat.getItems().size() + + " 种商品")); + g.setItem(48, page > 0 + ? mkItem(Material.ARROW, + "§7上一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(49, mkItem(Material.PAPER, + "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 + ? mkItem(Material.ARROW, + "§7下一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, + "§7返回商店")); + + p.openInventory(g); + } + + // ===== GUI: 退款 ===== + + public void openRefundPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§c§l退款中心"); + + // 位移检测:比对上次记录的背包内容 + validateRefunds(p); + + // 过期清理 + synchronized (refundRecords) { + refundRecords.removeIf(RefundRecord::isExpired); + } + + int idx = 0; + synchronized (refundRecords) { + for (RefundRecord r : refundRecords) { + if (idx >= 45) break; + if (!r.playerUUID.equals(p.getUniqueId())) continue; + if (r.isExpired()) continue; + + // 盒装用潜影盒图标,普通用商品图标 + Material iconMat = (r.shulkerMaterial != null) + ? r.shulkerMaterial : r.material; + + ItemStack is = new ItemStack(iconMat); + ItemMeta im = is.getItemMeta(); + if (im != null) { + int secs = r.getSecondsLeft(); + im.setDisplayName("§e" + r.itemName + + (r.shulkerMaterial != null + ? " §7[盒装]" : " x" + r.amount)); + + List lore = new ArrayList<>(); + lore.add("§7订单: §f#" + r.timestamp); + lore.add("§7支付: §a" + r.totalPaid + + "§7枚债券"); + + if (r.shulkerMaterial != null) { + int boxCount = countShulkers(p, + r.shulkerMaterial); + lore.add("§7潜影盒: §f" + + (boxCount >= 1 + ? "§a在背包中" + : "§c已移出")); + } else { + int have = countDirect(p, r.material); + lore.add("§7物品: §f" + have + + "/" + r.amount); + } + + lore.add("§7剩余: §e" + secs + "秒"); + lore.add(""); + lore.add("§c点击退款"); + im.setLore(lore); + } + is.setItemMeta(im); + g.setItem(10 + idx, is); + idx++; + } + } + + if (idx == 0) { + g.setItem(22, mkItem(Material.BARRIER, + "§7暂无可退款记录")); + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + + p.openInventory(g); + } + // 统计背包中某物品数量 + private int countInInventory(Player p, Material mat) { + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == mat) { + count += is.getAmount(); + } + } + // 潜影盒也要检查里面 + for (ItemStack is : p.getInventory().getContents()) { + if (is != null + && is.getType().name() + .endsWith("_SHULKER_BOX")) { + count += is.getAmount(); + } + } + return count; + } + + // 只统计背包中直接可见的数量(不含盒内) + // 新代码:增加按完整物品匹配的方法 + private int countDirect(Player p, Material mat) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.getType() == mat) + count += is.getAmount(); + } + return count; + } + + private int countDirectByStack(Player p, + ItemStack target) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.isSimilar(target)) + count += is.getAmount(); + } + return count; + } + // 统计背包中潜影盒数量 + private int countShulkers(Player p, Material shulker) { + if (shulker == null) return Integer.MAX_VALUE; + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == shulker) + count += is.getAmount(); + } + return count; + } + + // 退款检测:位移 OR 消耗 → 不能退 + private void validateRefunds(Player p) { + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { + it.remove(); + continue; + } + + if (r.shulkerMaterial != null) { + // 盒装:检测潜影盒是否还在背包 + int now = countShulkers( + p, r.shulkerMaterial); + if (now < 1) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 潜影盒已移出背包,无法退款"); + } + } else { + // 普通:检测商品是否还在背包 + int now = countDirect(p, r.material); + if (now < r.amount) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 物品已被使用或移出,无法退款"); + } + } + } + } + } + + + public int getPlayerPage(java.util.UUID uuid) { + return viewPage.getOrDefault(uuid, 0); + } + + public ShopCategory getPlayerCategory(java.util.UUID uuid) { + return viewCat.get(uuid); + } + + // ===== 供GUIManager调用:商店主界面点击 ===== + public boolean handleMainShopClick(Player p, int raw) { + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true; } + + if (raw == 48) { + toggleCartMode(p); + openShopMain(p); + return true; + } + if (raw == 50) { + openCart(p); + return true; + } + if (raw == 53) { + plugin.getGui().openMain(p); + return true; + } + + // ★ 新增:优惠券按钮 + if (raw == 47) { + openCouponInput(p); + return true; + } + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true;} + int idx = slotToIndex(raw); + if (idx < 0 || idx >= categories.size()) + return true; + openCategory(p, categories.get(idx), 0); + return true; + } + + // ===== 供GUIManager调用:退款中心点击 ===== + public boolean handleRefundClick(Player p, int raw) { + if (raw == 49) { openShopMain(p); return true; } + return true; + } + + // ===== 供GUIManager调用:分类页点击 ===== + public boolean handleCategoryClick(Player p, String title, + int raw, boolean left, + boolean shift) { + // plugin.getLogger().info("[Shop] ★ handleCategoryClick raw=" + raw); + + ShopManager sm = this; + ShopCategory match = null; + for (ShopCategory c : categories) { + if (title.equals("§6§l" + c.getName())) { + match = c; + break; + } + } + if (match == null) { + // plugin.getLogger().info("[Shop] ★ 匹配失败, title=[" + title + "]"); + return false; + } + // plugin.getLogger().info("[Shop] ★ 匹配到: " + match.getName()); + + viewCat.put(p.getUniqueId(), match); + + if (raw == 53) { openShopMain(p); return true; } + int pg = getPlayerPage(p.getUniqueId()); + if (raw == 48) { openCategory(p, match, pg - 1); return true; } + if (raw == 50) { openCategory(p, match, pg + 1); return true; } + + int itemIdx = slotToIndex(raw); + if (itemIdx < 0) { + // plugin.getLogger().info("[Shop] ★ slot=" + raw + " 不是商品格"); + return true; + } + int actual = itemIdx + pg * PAGE_SIZE; + if (actual < 0 || actual >= match.getItems().size()) { + // plugin.getLogger().info("[Shop] ★ 超出范围 actual=" + actual + " size=" + match.getItems().size()); + return true; + } + ShopItem si = match.getItems().get(actual); + // plugin.getLogger().info("[Shop] ★ 准备购买: " + si.getId() + " left=" + left); + + int amount = shift ? 64 : 1; + if (left) { + if (shift && isCartMode(p)) { addToCart(p, si); } + else if (shift) { buyItem(p, si, 64); } + else if (isCartMode(p)) { addToCart(p, si); } + else { buyItem(p, si, 1); } + } else { + if (shift) { sellAllOf(p, si); } + else { sellItem(p, si, 1); } + } + openCategory(p, match, pg); + return true; + } + + // ===== 批量出售某商品全部数量 ===== + private void sellAllOf(Player p, ShopItem item) { + if (item == null) return; + // ★ 补上禁止出售检查 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return; + } + if (item == null) return; + Material mat = item.getMaterial(); + int total = 0; + int count = 0; + // 遍历背包清点 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + total += is.getAmount(); + count++; + } + } + if (total <= 0) { + p.sendMessage("§c你没有可出售的 §e" + + item.getDisplayName()); + return; + } + // 清除背包中该物品 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + } + } + int money = item.getEffectiveSellPrice() * total; + plugin.getBonds().addBonds( + p.getName(), money, "shop_sell", + item.getId(), "商店系统", + "批量出售" + item.getDisplayName() + + " x" + total); + if (item.getStock() >= 0) + item.setStock(item.getStock() + total); + p.sendMessage("§a批量出售: §e" + + item.getDisplayName() + + " x" + total + + " §a+" + money + "枚债券"); + } + + + // ===== 定时 ===== + + private void startHourlyTask() { + new BukkitRunnable() { + @Override + public void run() { + // ★ 汇报交给 SalesStatsManager + if (plugin.getSalesStats() != null) { + plugin.getSalesStats().report(); + } + // 重置小时销量 + for (ShopCategory cat : categories) + for (ShopItem item : cat.getItems()) + item.setHourlySales(0); + saveAll(); + } + }.runTaskTimer(plugin, 72000L, 72000L); + } + + + private void startCleanupTask() { + new BukkitRunnable() { + @Override + public void run() { + synchronized (refundRecords) { + refundRecords.removeIf( + RefundRecord::isExpired); + } + } + }.runTaskTimer(plugin, 1200L, 1200L); + } + + // ===== 工具 ===== + + private int slotToIndex(int slot) { + for (int i = 0; i < ITEM_SLOTS.length; i++) + if (ITEM_SLOTS[i] == slot) return i; + return -1; + } + + private ItemStack mkItem(Material mat, + String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src/main/java/Sdf1_login/TicketManager.java b/backup/src/main/java/Sdf1_login/TicketManager.java new file mode 100644 index 0000000..c21edc2 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/TicketManager.java @@ -0,0 +1,1644 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class TicketManager { + + private final Main plugin; + + public static final String T_TICKET_MAIN = + "§6§l工单中心"; + public static final String T_TICKET_CREATE = + "§e§l创建工单"; + public static final String T_TICKET_MY = + "§b§l我的工单"; + public static final String T_TICKET_ADMIN = + "§c§l工单管理"; + public static final String T_TICKET_DETAIL = + "§a§l工单详情"; + + public static final String TYPE_BUG = + "bug反馈"; + public static final String TYPE_HELP = + "求助"; + public static final String TYPE_REPORT = + "举报"; + public static final String TYPE_APPLY = + "申请"; + public static final String TYPE_OTHER = + "其他"; + public static final String TYPE_DELETE = + "删号"; + public static final String TYPE_PASSWORD = + "修改密码"; + + + + private static final Map + PRICES = new LinkedHashMap<>(); + static { + PRICES.put(TYPE_BUG, 10); + PRICES.put(TYPE_HELP, 5); + PRICES.put(TYPE_REPORT, 8); + PRICES.put(TYPE_APPLY, 15); + PRICES.put(TYPE_OTHER, 5); + PRICES.put(TYPE_DELETE, 0); + PRICES.put(TYPE_PASSWORD, 0); + } + + + public TicketManager(Main plugin) { + this.plugin = plugin; + } + + // ========== GUI ========== + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_MAIN); + fillBg(g); + g.setItem(10, mk(Material.PAPER, + "§e创建工单", + "§7提交bug、求助、举报等")); + g.setItem(12, mk(Material.BOOK, + "§b我的工单", + "§7查看已提交的工单")); + g.setItem(14, mk(Material.CHEST, + "§6工单记录", + "§7查看历史工单")); + if (adm(p)) { + g.setItem(16, mk(Material.REDSTONE, + "§c工单管理", + "§7管理员专用")); + } + if (plugin.getDb() + .isServiceProvider( + p.getName())) { + g.setItem(18, mk(Material.EMERALD, + "§a服务商面板", + "§7接单、处理工单")); + } + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openCreate(Player p) { + Inventory g = Bukkit.createInventory( + null, 36, T_TICKET_CREATE); + fillBg(g); + g.setItem(10, mk(Material.BOOK, + "§cbug反馈")); + g.setItem(11, mk(Material.EMERALD, + "§a求助")); + g.setItem(12, mk(Material.BLAZE_POWDER, + "§6举报")); + g.setItem(13, mk(Material.NAME_TAG, + "§e申请")); + g.setItem(14, mk(Material.PAPER, + "§7其他")); + g.setItem(20, mk(Material.BARRIER, + "§c申请删号", + "§7申请删除账号")); + g.setItem(21, mk(Material.NETHER_WART, + "§d申请改密", + "§7请求管理员协助改密")); + g.setItem(31, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyTickets(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_MY); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByPlayer( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openDetail(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_DETAIL); + fillBg(g); + + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = sv(t, "requester"); + String title = sv(t, "title"); + String desc = sv(t, "description"); + String assigned = sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + int confirmed = nv(t, "admin_confirmed"); + + g.setItem(10, mk(Material.PAPER, + "§e§l" + (title.isEmpty() + ? type : title), + "§7类型: §f" + type, + "§7状态: " + stxt(status), + "§7提交者: §f" + requester, + "§7奖励: §e" + reward + " 积分")); + + g.setItem(12, mk(Material.BOOK, + "§7详细描述", + desc.isEmpty() ? "§7无描述" + : "§7" + (desc.length() > 40 + ? desc.substring(0, 40) + + "..." : desc))); + + if (!assigned.isEmpty()) { + g.setItem(14, mk(Material.PLAYER_HEAD, + "§7处理人: §f" + assigned, + confirmed == 1 + ? "§7评分: §e" + score + : "§7处理中")); + } else { + g.setItem(14, mk(Material.BARRIER, + "§7未分配")); + } + + // 驳回原因 + if ("closed".equals(status)) { + String reason = sv(t, "reject_reason"); + if (!reason.isEmpty()) { + g.setItem(13, mk(Material.BARRIER, + "§c驳回原因", + "§7" + reason)); + } + } + + // 回复按钮(非关闭/已完结状态) + if (!"closed".equals(status) + && !"withdrawn".equals(status) + && !"resolved".equals(status)) { + g.setItem(15, mk( + Material.WRITABLE_BOOK, + "§b回复工单", + "§7向对方发送消息")); + g.setItem(16, mk(Material.BOOK, + "§7查看回复记录")); + } + + // ===== 管理员按钮 ===== + if (adm(p)) { + if ("pending".equals(status)) { + // 待分配:可分配给服务商 + g.setItem(19, mk(Material.EMERALD, + "§a分配工单", + "§7指定服务商处理")); + // 也可自己处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && !assigned.equals("admin") + && !assigned.isEmpty()) { + // 服务商在处理:可取消指派收回 + g.setItem(19, mk(Material.BARRIER, + "§c取消指派", + "§7取消服务商指派,收回工单")); + // 也可自己接手处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && assigned.equals("admin")) { + // 管理员自己在处理:可标记完结 + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + if ("replied".equals(status) + && assigned.equals("admin")) { + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + // 驳回(非已关闭/已完结状态) + if (!"closed".equals(status) + && !"resolved".equals(status) + && !"withdrawn".equals(status)) { + g.setItem(21, mk(Material.RED_DYE, + "§c驳回工单", + "§7驳回并关闭此工单")); + } + } + + // ===== 服务商按钮 ===== + if (plugin.getDb() + .isServiceProvider(p.getName()) + && !TYPE_DELETE.equals(type) + && !TYPE_PASSWORD.equals(type)) { + if ("pending".equals(status) + && assigned.isEmpty()) { + // 待分配无人:抢单 + g.setItem(19, mk( + Material.GOLD_INGOT, + "§6抢单", + "§7接取此工单")); + } + if (assigned.equals(p.getName()) + && ("processing".equals(status) + || "replied".equals(status))) { + // 我在处理:标记完结/上报异常 + g.setItem(19, mk( + Material.LIME_DYE, + "§a标记完结", + "§7上报处理完成")); + g.setItem(20, mk( + Material.REDSTONE, + "§c上报异常", + "§7报告处理中的问题")); + } + } + + g.setItem(22, mk(Material.ARROW, + "§7返回")); + + ItemStack info = g.getItem(10); + if (info != null) { + ItemMeta im = info.getItemMeta(); + if (im != null) { + im.setCustomModelData(tid); + info.setItemMeta(im); + } + } + p.openInventory(g); + } + + + public void openAdminList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_ADMIN); + fillBg(g); + g.setItem(45, mk(Material.EMERALD, + "§a邀请服务商")); + g.setItem(46, mk(Material.BARRIER, + "§c移除服务商")); + g.setItem(47, mk(Material.CLOCK, + "§e服务商列表")); + g.setItem(48, mk(Material.NETHER_STAR, + "§e直接派单", + "§7管理员直接创建并派单")); + List> all = + new ArrayList<>(); + all.addAll(plugin.getDb() + .getTicketsByStatus("pending")); + all.addAll(plugin.getDb() + .getTicketsByStatus("processing")); + all.addAll(plugin.getDb() + .getTicketsByStatus("replied")); + all.addAll(plugin.getDb() + .getTicketsByStatus("completed")); + all.addAll(plugin.getDb() + .getTicketsByStatus("resolved")); + all.addAll(plugin.getDb() + .getTicketsByStatus("closed")); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + + public void openProviderPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§d§l服务商面板"); + fillBg(g); + g.setItem(10, mk(Material.CHEST, + "§e抢单大厅", + "§7查看可接工单")); + g.setItem(12, mk(Material.CLOCK, + "§b我的工单", + "§7我正在处理的工单")); + g.setItem(14, mk(Material.PAPER, + "§6历史工单", + "§7已完成的工单")); + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openGrabList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l抢单大厅"); + fillBg(g); + List> pending = + plugin.getDb() + .getTicketsByStatus( + "pending"); + List> list = + new ArrayList<>(); + for (Map t : pending) { + if (sv(t, "assigned_to").isEmpty()) { + list.add(t); + } + } + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyAssigned(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§b§l我处理中的工单"); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByProvider( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + // ========== 业务 ========== + + public int submitTicket(Player p, + String type, String title, + String desc) { + int price = PRICES + .getOrDefault(type, 5); + int id = plugin.getDb().createTicket( + type, "pending", "normal", + p.getName(), title, desc, + price, ""); + if (id > 0) { + if (TYPE_DELETE.equals(type) + || TYPE_PASSWORD.equals(type)) { + plugin.getDb().updateTicketField( + id, "assigned_to", "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage( + "§a§l[工单] §f工单 #" + + id + " 已提交," + + "等待管理员处理"); + } else { + p.sendMessage( + "§a§l[工单] §f创建成功!" + + " #§e" + id + + " 奖励: §e" + + price + "积分"); + } + notifyAdmins(p.getName(), id); + } else { + p.sendMessage("§c创建失败"); + } + return id; + } + + + public void adminCreateAndAssign( + Player p, String type, + String title, String desc, + String provider, int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + int id = plugin.getDb().createTicket( + type, "processing", "normal", + p.getName(), title, desc, + price, ""); + if (id <= 0) { + p.sendMessage("§c创建失败"); + return; + } + plugin.getDb().assignTicket( + id, provider); + if (price > 0) { + plugin.getDb().updateTicketField( + id, "reward_amount", price); + } + p.sendMessage("§a工单 #" + id + + " 已创建并分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f管理员指派工单 #" + + id + " 给您"); + playAssignedSound(tp); + } + } + + public void grabTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!"pending".equals( + sv(t, "status"))) { + p.sendMessage("§c该工单已被接单"); + return; + } + if (!sv(t, "assigned_to").isEmpty()) { + p.sendMessage("§c该工单已被分配"); + return; + } + plugin.getDb().assignTicket( + tid, p.getName()); + p.sendMessage("§a抢单成功!工单 #" + tid); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f您的工单 #" + + tid + " 已被 " + + p.getName() + + " 接单"); + } + } + + public void assignToProvider(Player p, + int tid, String provider, + int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + plugin.getDb().assignTicket( + tid, provider); + if (price > 0) { + plugin.getDb().updateTicketStatus( + tid, "processing"); + plugin.getDb().updateTicketField( + tid, "reward_amount", price); + } + p.sendMessage("§a工单 #" + tid + + " 已分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f您有新工单: #" + + tid); + playAssignedSound(tp); + } + } + + public void confirmAndScore(Player p, + int tid, int score) { + if (score < 1 || score > 5) { + p.sendMessage("§c评分1-5"); + return; + } + plugin.getDb().confirmTicket( + tid, score); + p.sendMessage("§a工单 #" + tid + + " 已确认,评分: " + score); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已确认完结"); + } + } + settleTicket(tid); + } + + public void completeAsProvider(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "assigned_to") + .equals(p.getName())) { + p.sendMessage("§c这不是你的工单"); + return; + } + plugin.getDb().completeTicket(tid); + p.sendMessage("§a工单 #" + tid + + " 已标记完结,等待确认"); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已处理完成"); + } + } + + public void rejectTicket(Player p, + int tid, String reason) { + plugin.getDb().updateTicketStatus( + tid, "closed"); + plugin.getDb().updateTicketField( + tid, "reject_reason", reason); + p.sendMessage("§c工单 #" + tid + + " 已驳回并关闭"); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 被驳回: " + + reason); + } + } + } + + public void replyTicket(Player p, + int tid, String message) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + String status = sv(t, "status"); + if ("closed".equals(status) + || "withdrawn".equals(status) + || "resolved".equals(status)) { + p.sendMessage("§c该工单已关闭"); + return; + } + plugin.getDb().addTicketReply( + tid, p.getName(), message); + p.sendMessage("§a回复成功"); + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + String sender = p.getName(); + if (!requester.equals(sender)) { + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + if (!assigned.isEmpty() + && !assigned.equals(sender)) { + Player ap = Bukkit.getPlayer( + assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + } + + public void showReplies(Player p, + int tid) { + List> replies = + plugin.getDb() + .getTicketReplies(tid); + if (replies.isEmpty()) { + p.sendMessage("§7暂无回复"); + return; + } + p.sendMessage("§e§l=== 回复记录 ==="); + SimpleDateFormat sdf = + new SimpleDateFormat( + "MM-dd HH:mm"); + for (Map r : replies) { + String sender = sv(r, "sender"); + String msg2 = sv(r, "message"); + long time = ((Number) r.get( + "create_time")).longValue(); + p.sendMessage("§7[" + sdf.format( + new Date(time)) + "] §f" + + sender + "§7: " + msg2); + } + p.sendMessage( + "§e§l================="); + } + + public void withdrawTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "requester") + .equals(p.getName()) + && !adm(p)) { + p.sendMessage("§c无权操作"); + return; + } + plugin.getDb().updateTicketStatus( + tid, "withdrawn"); + p.sendMessage("§a工单 #" + tid + + " 已撤回"); + } + + public void showProviderList(Player p) { + List> list = + plugin.getDb() + .getAllServiceProviders(); + if (list.isEmpty()) { + p.sendMessage("§7暂无服务商"); + return; + } + p.sendMessage( + "§e§l=== 服务商列表 ==="); + for (Map pro : list) { + p.sendMessage("§7- §f" + + pro.get("player_name") + + " §7角色: §e" + + pro.get("role")); + } + p.sendMessage( + "§e§l==================="); + } + + public void autoProcessCompleted() { + long timeoutMs = 10 * 60 * 1000L; + + // 自动处理删号工单(5分钟自动通过) + List> deleteTickets = + plugin.getDb() + .getTicketsByStatus( + "processing"); + for (Map t : deleteTickets) { + String dtype = sv(t, "type"); + if (!TYPE_DELETE.equals(dtype)) continue; + int did = nv(t, "id"); + int dconf = nv(t, "admin_confirmed"); + // 已经处理过了跳过 + if (dconf != 0) continue; + String dasg = sv(t, "assigned_to"); + // 只处理分配给admin的 + if (!"admin".equals(dasg)) continue; + long createTime = ((Number) t.get( + "create_time")).longValue(); + if (System.currentTimeMillis() + - createTime >= 5 * 60 * 1000L) { + processDeleteAccount(did, t); + } + } + + + // 已完结未确认→10分钟自动完结 + List> list = + plugin.getDb() + .getUnconfirmedCompleted( + timeoutMs); + for (Map t : list) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 已自动完结"); + String assigned = + sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) + pp.sendMessage( + "§e§l[工单] §f工单 #" + + id + + " 已自动完结"); + } + settleTicket(id); + } + + // 已回复但10分钟无互动→自动完结 + List> stale = + plugin.getDb() + .getRepliedStaleTickets( + timeoutMs); + for (Map t : stale) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 超时自动完结"); + settleTicket(id); + } + } + + + + // ========== GUI点击 ========== + public boolean handleClick(Player p, + String title, int slot) { + + if (title.equals(T_TICKET_MAIN)) { + if (slot == 10) openCreate(p); + else if (slot == 12) + openMyTickets(p); + else if (slot == 14) + openMyTickets(p); + else if (slot == 16) { + if (adm(p)) openAdminList(p); + else p.sendMessage("§c权限不足"); + } + else if (slot == 18) { + if (plugin.getDb() + .isServiceProvider(p.getName())) { + openProviderPanel(p); + } else { + p.sendMessage("§c权限不足"); + } + } + else if (slot == 22) + plugin.getGui().openMain(p); + return true; + } + + if (title.equals(T_TICKET_CREATE)) { + if (slot == 31) { + openMain(p); + return true; + } + String type = null; + if (slot == 10) type = TYPE_BUG; + else if (slot == 11) type = TYPE_HELP; + else if (slot == 12) type = TYPE_REPORT; + else if (slot == 13) type = TYPE_APPLY; + else if (slot == 14) type = TYPE_OTHER; + else if (slot == 20) type = TYPE_DELETE; + else if (slot == 21) type = TYPE_PASSWORD; + if (type != null) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_TITLE; + plugin.getChatInput() + .getState(p) + .ticketType = type; + if (TYPE_DELETE.equals(type)) { + p.sendMessage("§e请描述删号原因:"); + } else if (TYPE_PASSWORD.equals(type)) { + p.sendMessage("§e请描述需要协助的内容:"); + } else { + p.sendMessage("§e请输入工单标题:"); + } + } + return true; + } + + + if (title.equals(T_TICKET_MY)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals(T_TICKET_DETAIL)) { + return handleClickDetail(p, slot); + } + + if (title.equals(T_TICKET_ADMIN)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot == 45) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_INVITE_PROVIDER; + p.sendMessage("§e输入服务商玩家名:"); + return true; + } + if (slot == 46) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REMOVE_PROVIDER; + p.sendMessage("§e输入要移除的服务商:"); + return true; + } + if (slot == 47) { + showProviderList(p); + return true; + } + if (slot == 48) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ADMIN_TYPE; + p.sendMessage("§e选择工单类型:"); + p.sendMessage("§7bug/求助/举报/申请/其他"); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§d§l服务商面板")) { + if (slot == 22) { + plugin.getGui().openMain(p); + return true; + } + if (slot == 10) openGrabList(p); + else if (slot == 12) openMyAssigned(p); + else if (slot == 14) openMyTickets(p); + return true; + } + + if (title.equals("§6§l抢单大厅")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§b§l我处理中的工单")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + return false; + } + + private boolean handleClickDetail(Player p, + int slot) { + if (slot == 22) { + openMyTickets(p); + return true; + } + int id = gtFromDetail(p); + if (id <= 0) return true; + Map t = + plugin.getDb().getTicket(id); + if (t == null) return true; + + String st = sv(t, "status"); + int conf = nv(t, "admin_confirmed"); + String asg = sv(t, "assigned_to"); + + // 回复按钮 + if (slot == 15) { + if (!"closed".equals(st) + && !"withdrawn".equals(st) + && !"resolved".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPLY; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入回复内容:"); + } + return true; + } + + // 查看回复记录 + if (slot == 16) { + showReplies(p, id); + return true; + } + + // slot 19 + if (slot == 19) { + // 管理员:分配工单 + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ASSIGN; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage( + "§e输入: 服务商名 价格"); + return true; + } + // 管理员:取消指派(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + cancelTicket(p, id); + openDetail(p, id); + return true; + } + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + Map tk = + plugin.getDb().getTicket(id); + String tkType = sv(tk, "type"); + if (TYPE_DELETE.equals(tkType)) { + approveDeleteTicket(p, id); + return true; + } + plugin.getDb().completeTicket(id); + p.sendMessage("§a工单 #" + id + + " 已标记完结"); + openDetail(p, id); + return true; + } + } + // 服务商:抢单 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && "pending".equals(st) + && asg.isEmpty()) { + grabTicket(p, id); + openDetail(p, id); + return true; + } + // 服务商:标记完结 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + completeAsProvider(p, id); + openDetail(p, id); + return true; + } + return true; + } + + // slot 20 + if (slot == 20) { + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:自己接手(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 服务商:上报异常 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPORT_ISSUE; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入异常描述:"); + return true; + } + return true; + } + + // slot 21:驳回 + if (slot == 21) { + if (adm(p) && !"closed".equals(st) + && !"resolved".equals(st) + && !"withdrawn".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REJECT; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入驳回原因:"); + } + return true; + } + + return true; + } + + // ========== 聊天输入 ========== + + public boolean handleChatInput(Player p, + String msg, + ChatInputManager.InputType type) { + switch (type) { + case TICKET_TITLE: { + plugin.getChatInput() + .getState(p) + .ticketTitle = msg; + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return true; + } + case TICKET_DESC: { + String tt = plugin.getChatInput() + .getState(p).ticketType; + String tl = plugin.getChatInput() + .getState(p).ticketTitle; + plugin.getChatInput().reset(p); + submitTicket(p, tt, tl, msg); + return true; + } + case TICKET_SCORE: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + Map tk = + plugin.getDb() + .getTicket(tid2); + String st = tk != null + ? sv(tk, "status") + : ""; + if ("completed".equals(st)) { + plugin.getChatInput() + .reset(p); + confirmAndScore( + p, tid2, sc); + } else { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c工单尚未处理完成"); + } + } catch ( + NumberFormatException e) { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return true; + } + case TICKET_REJECT: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + plugin.getChatInput().reset(p); + rejectTicket(p, tid2, msg); + return true; + } + case TICKET_ASSIGN: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + plugin.getChatInput().reset(p); + assignToProvider(p, tid2, + prov, price); + runSync(() -> + openDetail(p, tid2)); + return true; + } + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return true; + } + plugin.getDb() + .addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return true; + } + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .isServiceProvider( + tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return true; + } + plugin.getDb() + .removeServiceProvider( + tgt); + p.sendMessage( + "§a已移除 " + tgt); + return true; + } + default: + return false; + } + } + + // ========== 私有 ========== + + private void settleTicket(int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) return; + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + ConfigManager cfg = + plugin.getConfig2(); + if (reward > 0) { + plugin.getDb().addPoints( + requester, reward); + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f获得 " + + reward + + " 积分奖励"); + } + } + if (!assigned.isEmpty()) { + int pts = (int) (score + * cfg.providerPointsPerScore); + if (pts > 0) { + plugin.getDb().addPoints( + assigned, pts); + } + double money = + cfg.baseEconomyReward + + score + * cfg.providerEconomyPerScore; + boolean ok = false; + try { + if (plugin.getEconomy() != null) { + OfflinePlayer op = + Bukkit.getOfflinePlayer( + assigned); + plugin.getEconomy() + .depositPlayer( + op, money); + ok = true; + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 经济失败: " + + e.getMessage()); + } + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) { + pp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 确认完结"); + if (pts > 0) + pp.sendMessage("§7积分: §e" + + pts); + if (ok) + pp.sendMessage("§7经济: §a$" + + String.format( + "%.0f", money)); + } + } + } + + private void notifyAdmins(String creator, + int tid) { + String tag = + plugin.getConfig2().adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§e§l[工单] §f" + creator + + " 提交了新工单 #" + + tid); + } + } + } + + private void playAssignedSound(Player p) { + for (int i = 0; i < 3; i++) { + final int d = i * 15; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline()) { + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + }, d); + } + } + + private void runSync(Runnable task) { + Bukkit.getScheduler() + .runTask(plugin, task); + } + + private boolean adm(Player p) { + return p.getScoreboardTags().contains( + plugin.getConfig2().adminTag); + } + + private void fillBg(Inventory g) { + ItemStack glass = mk( + Material.GRAY_STAINED_GLASS_PANE, + " "); + for (int i = 0; i < g.getSize(); i++) { + if (g.getItem(i) == null + || g.getItem(i).getType() + == Material.AIR) { + g.setItem(i, glass); + } + } + } + + private void fillList(Inventory g, + List> list) { + if (list.isEmpty()) { + g.setItem(22, mk(Material.BARRIER, + "§7暂无工单")); + return; + } + int slot = 0; + for (Map t : list) { + if (slot >= 45) break; + g.setItem(slot, makeItem(t)); + slot++; + } + } + /** + * 执行删号流程 + */ + /** + * 执行删号流程 + */ + public void processDeleteAccount(int tid, + Map t) { + String requester = sv(t, "requester"); + plugin.getDb().updateTicketStatus( + tid, "resolved"); + plugin.getDb().updateTicketField(tid, + "admin_confirmed", 1); + Player target = + Bukkit.getPlayer(requester); + if (target != null && target.isOnline()) { + plugin.backupInventory(target); + target.sendMessage( + "§a§l[工单] §f您的删号请求已通过"); + target.sendMessage( + "§7物品已备份,下次登录可取回"); + target.kickPlayer( + "§a删号成功,物品已备份。\n" + + "请等待5分钟后重新注册取回物品。"); + } else { + // 离线:仅标记,不操作数据库 + plugin.getLogger().info( + "[Sdf1_login] " + requester + + " 删号工单已通过,等待上线执行"); + } + // 延迟执行删号 + final String name = requester; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + plugin.getDb().deleteUser(name); + plugin.getLogger().info( + "[Sdf1_login] " + name + + " 删号工单已执行"); + }, 100L); + } + + /** + * 管理员手动批准删号 + */ + public void approveDeleteTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!TYPE_DELETE.equals(sv(t, "type"))) { + p.sendMessage("§c这不是删号工单"); + return; + } + p.sendMessage("§a工单 #" + tid + + " 已批准,正在执行删号..."); + processDeleteAccount(tid, t); + } + + /** + * 管理员取消工单(服务商异常后) + */ + /** + * 管理员取消工单(解除服务商绑定,管理员可自行处理) + */ + public void cancelTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + // 解除服务商绑定,状态回退到pending + plugin.getDb().updateTicketField( + tid, "assigned_to", ""); + plugin.getDb().updateTicketStatus( + tid, "pending"); + p.sendMessage("§a工单 #" + tid + + " 已取消指派,回到待分配状态"); + // 通知原服务商 + String assigned = sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player ap = Bukkit.getPlayer(assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 已被管理员取消指派"); + } + } + } + + + /** + * 服务商查看历史工单 + */ + public void openProviderHistory(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l我的历史工单"); + fillBg(g); + List> all = + plugin.getDb() + .getTicketsByProviderAll( + p.getName()); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + private ItemStack makeItem( + Map t) { + int id = nv(t, "id"); + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = + sv(t, "requester"); + String title = sv(t, "title"); + int reward = nv(t, "reward_amount"); + Material mat; + switch (status) { + case "processing": + mat = Material.YELLOW_DYE; + break; + case "replied": + mat = Material.CYAN_DYE; + break; + case "completed": + mat = Material.LIME_DYE; + break; + case "resolved": + mat = Material.LIME_DYE; + break; + case "closed": + mat = Material.GRAY_DYE; + break; + case "rejected": + mat = Material.RED_DYE; + break; + case "withdrawn": + mat = Material.GRAY_DYE; + break; + default: + mat = Material.ORANGE_DYE; + break; + } + ItemStack item = new ItemStack(mat); + ItemMeta im = item.getItemMeta(); + if (im != null) { + im.setDisplayName("§e#" + id + + " " + (title.isEmpty() + ? type : title)); + List lore = + new ArrayList<>(); + lore.add("§7类型: §f" + type); + lore.add("§7状态: " + + stxt(status)); + lore.add("§7提交者: §f" + + requester); + lore.add("§7奖励: §e" + reward + + " 积分"); + lore.add(""); + lore.add("§7点击查看详情"); + im.setLore(lore); + im.setCustomModelData(id); + item.setItemMeta(im); + } + return item; + } + + private int gt(ItemStack item) { + if (item == null) return -1; + ItemMeta im = item.getItemMeta(); + if (im == null) return -1; + if (im.hasCustomModelData()) + return im.getCustomModelData(); + return -1; + } + + private int gtFromDetail(Player p) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(10); + return gt(it); + } + + private String stxt(String status) { + switch (status) { + case "pending": + return "§e待处理"; + case "processing": + return "§b处理中"; + case "replied": + return "§9已回复"; + case "completed": + return "§a待确认"; + case "resolved": + return "§a已完结"; + case "closed": + return "§7已关闭"; + case "rejected": + return "§c已驳回"; + case "withdrawn": + return "§7已撤回"; + default: + return "§7" + status; + } + } + + private String sv(Map m, + String key) { + Object v = m.get(key); + return v != null + ? String.valueOf(v) : ""; + } + + private int nv(Map m, + String key) { + Object v = m.get(key); + return v instanceof Number + ? ((Number) v).intValue() : 0; + } + + private ItemStack mk(Material mat, + String name, String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) { + List list = + new ArrayList<>(); + for (String l : lore) { + if (!l.isEmpty()) + list.add(l); + } + if (!list.isEmpty()) + im.setLore(list); + } + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src/main/java/Sdf1_login/UpdateChecker.java b/backup/src/main/java/Sdf1_login/UpdateChecker.java new file mode 100644 index 0000000..c5c01bb --- /dev/null +++ b/backup/src/main/java/Sdf1_login/UpdateChecker.java @@ -0,0 +1,523 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; + +public class UpdateChecker { + + private final Main plugin; + private final String currentVersion; + + // ===== 仓库信息 ===== + private static final String GITHUB_OWNER = + "ypsdf1"; + private static final String GITHUB_REPO = + "sdf1_login"; + private static final String GITEE_OWNER = + "nihaoshidifu"; + private static final String GITEE_REPO = + "sdf1_login"; + + + // API 地址 + private static final String GITHUB_API = + "https://api.github.com/repos/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + + "/tags"; + private static final String GITEE_API = + "https://gitee.com/api/v5/repos/" + + GITEE_OWNER + "/" + + GITEE_REPO + + "/tags"; + + // 仓库页面链接(给用户看的) + private static final String GITHUB_LINK = + "https://github.com/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + "/releases"; + private static final String GITEE_LINK = + "https://gitee.com/" + + GITEE_OWNER + "/" + + GITEE_REPO + "/releases"; + + + // 单次请求超时(毫秒) + private static final int TIMEOUT_MS = 3000; + + // 熔断阈值 + private static final int TRIP_THRESHOLD = 5; + + // ===== 熔断状态(内存态,重启自动清除) ===== + private volatile boolean circuitTripped = + false; + private volatile int failCount = 0; + + // ===== 自动检测禁用(持久化到文件) ===== + private volatile boolean autoCheckDisabled = + false; + private final File disabledFile; + + public UpdateChecker(Main plugin) { + this.plugin = plugin; + this.currentVersion = + plugin.getDescription() + .getVersion(); + this.disabledFile = new File( + plugin.getDataFolder(), + "update_disabled.dat"); + loadDisabledState(); + } + + // ========== 持久化禁用状态 ========== + + private void loadDisabledState() { + if (disabledFile.exists()) { + try (BufferedReader br = + new BufferedReader( + new FileReader( + disabledFile))) { + String line = br.readLine(); + if (line != null) { + autoCheckDisabled = + "true".equals( + line.trim()); + } + } catch (Exception ignored) { + } + } + } + + private void saveDisabledState() { + try (PrintWriter pw = + new PrintWriter( + new FileWriter( + disabledFile))) { + pw.println(autoCheckDisabled + ? "true" : "false"); + } catch (Exception ignored) { + } + } + + // ========== 公开方法 ========== + + /** + * onEnable 调用(异步静默) + * 熔断或禁用则直接跳过 + */ + public void checkOnEnable() { + if (autoCheckDisabled) return; + if (circuitTripped) return; + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doCheck(null, true)); + } + + /** + * /sdf1_login update 调用(异步) + * 忽略熔断和禁用状态,强制检测 + * 只有拿到版号才解除熔断 + */ + public void checkUpdate( + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> + doCheck(sender, + false)); + } + + // ========== 核心检测逻辑 ========== + private void doCheck(CommandSender sender, + boolean isAuto) { + + if (isAuto) { + if (autoCheckDisabled + || circuitTripped) { + return; + } + } + + // 通道1:GitHub + CheckResult gh = + tryChannel(GITHUB_API, + "GitHub", GITHUB_LINK); + + // 通道2:Gitee + CheckResult ge = + tryChannel(GITEE_API, + "Gitee", GITEE_LINK); + + // 选择成功的结果 + CheckResult chosen = null; + String failMsg = null; + + if (gh.isSuccess()) { + chosen = gh; + } else if (ge.isSuccess()) { + chosen = ge; + } else { + // 双通道均失败 + failCount++; + // 拼接两个通道的失败原因 + StringBuilder sb = + new StringBuilder(); + if (gh.error != null) { + sb.append("§7GitHub: §c") + .append(gh.error); + } + if (ge.error != null) { + if (sb.length() > 0) + sb.append("\n"); + sb.append("§7Gitee: §c") + .append(ge.error); + } + if (failCount >= TRIP_THRESHOLD) { + circuitTripped = true; + plugin.getLogger().warning( + "[Sdf1_login] 更新检测" + + "已熔断,连续失败" + + failCount + "次"); + } + if (sender != null) { + final String msg = + sb.toString(); + final boolean tripped = + circuitTripped; + final int fc = failCount; + Bukkit.getScheduler() + .runTask(plugin, () -> { + sender.sendMessage( + "§c§l[更新] " + + "检测失败"); + sender.sendMessage(msg); + sender.sendMessage( + "§e失败次数: " + + fc + "/" + + TRIP_THRESHOLD); + if (tripped) { + sender.sendMessage( + "§c已熔断," + + "请重启服务器" + + "重置"); + } + }); + } + return; + } + + // 至少一个通道成功 + failCount = 0; + + // 检查禁用信号 + if (isDisableSignal(chosen.version)) { + autoCheckDisabled = true; + saveDisabledState(); + plugin.getLogger().info( + "[Sdf1_login] 远程版本信号" + + " \"" + chosen.version + + "\",自动更新检测已禁用"); + if (sender != null) { + Bukkit.getScheduler() + .runTask(plugin, () -> + sender.sendMessage( + "§e[更新] 远程已" + + "禁用自动" + + "更新检测")); + } + return; + } + + // 手动调用:成功才解除熔断和禁用 + if (!isAuto) { + if (circuitTripped) { + circuitTripped = false; + } + if (autoCheckDisabled) { + autoCheckDisabled = false; + saveDisabledState(); + } + } + + final String src = chosen.source; + final String lnk = chosen.link; + final String rv = chosen.version; + + Bukkit.getScheduler() + .runTask(plugin, () -> { + if (sender == null) return; + + sender.sendMessage( + "§e[更新] 检测成功" + + " via: §a" + src); + sender.sendMessage( + "§e[更新] 本地版本:" + + " §f" + + currentVersion); + sender.sendMessage( + "§e[更新] 远程版本:" + + " §f" + rv); + + if (isNewer(rv, + currentVersion)) { + sender.sendMessage( + "§c[更新] 发现新" + + "版本: " + rv); + sender.sendMessage( + "§a请前往仓库下载" + + "更新:"); + sender.sendMessage( + "§a" + lnk); + } else { + sender.sendMessage( + "§a[更新] 已是" + + "最新版本"); + } + + // 显示另一通道的失败原因 + // (仅信息,不影响结果) + CheckResult other = + (src.equals("GitHub")) + ? ge : gh; + if (!other.isSuccess() + && other.error != null) { + sender.sendMessage( + "§7[更新] " + + other.source + + "不可用: " + + other.error); + } + }); + } + + + // ========== 禁用信号判断 ========== + + private boolean isDisableSignal( + String version) { + if (version == null) return false; + String v = version.trim().toLowerCase(); + return "999".equals(v) + || "关闭".equals(v) + || "false".equals(v) + || "禁用".equals(v); + } + + // ========== HTTP 请求(带超时) ========== + + // ========== 检测结果 ========== + + private static class CheckResult { + final String version; + final String source; + final String link; + final String error; + + CheckResult(String version, + String source, + String link, + String error) { + this.version = version; + this.source = source; + this.link = link; + this.error = error; + } + + boolean isSuccess() { + return version != null; + } + } + +// ========== HTTP 请求 ========== + + private CheckResult tryChannel( + String apiUrl, + String sourceName, + String link) { + HttpURLConnection conn = null; + try { + URL url = new URL(apiUrl); + conn = (HttpURLConnection) + url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; " + + "Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) " + + "Chrome/120.0.0.0 Safari/537.36"); + conn.setRequestProperty( + "Accept", + "*/*"); + conn.setRequestProperty( + "Accept-Language", + "zh-CN,zh;q=0.9"); + conn.setConnectTimeout(TIMEOUT_MS); + conn.setReadTimeout(TIMEOUT_MS); + conn.setInstanceFollowRedirects(true); + + int code = conn.getResponseCode(); + + if (code == 404) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 未找到 (HTTP 404)"); + } + if (code == 403) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 被限流 (HTTP 403)"); + } + if (code != 200) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": HTTP " + code); + } + + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn + .getInputStream())); + StringBuilder sb = + new StringBuilder(); + String line; + while ((line = reader.readLine()) + != null) { + sb.append(line); + } + reader.close(); + + String tag = + parseTagName(sb.toString()); + if (tag == null) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 无法解析版本号"); + } + + return new CheckResult(tag, + sourceName, link, null); + + } catch (java.net + .SocketTimeoutException e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": 连接超时"); + } catch (java.net.ConnectException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 连接被拒绝"); + } catch (java.net + .UnknownHostException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": DNS解析失败"); + } catch (Exception e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": " + + e.getMessage()); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + + + /** + * 从 JSON 中提取 tag_name + */ + private String parseTagName(String json) { + // tags API 返回数组: [{"name":"v1.1",...}] + // releases API 返回对象: {"tag_name":"v1.1"} + + // 尝试 tag_name(releases格式) + int idx = + json.indexOf("\"tag_name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + // 尝试 "name"(tags格式) + idx = json.indexOf("\"name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + return null; + } + + private String cleanTag(String tag) { + if (tag.startsWith("v") + || tag.startsWith("V")) { + tag = tag.substring(1); + } + return tag.isEmpty() ? null : tag; + } + + + // ========== 版本比较 ========== + + private boolean isNewer(String remote, + String local) { + String[] r = remote.split("\\."); + String[] l = local.split("\\."); + int len = Math.max(r.length, l.length); + for (int i = 0; i < len; i++) { + int ri = i < r.length + ? parseNum(r[i]) : 0; + int li = i < l.length + ? parseNum(l[i]) : 0; + if (ri > li) return true; + if (ri < li) return false; + } + return false; + } + + private int parseNum(String s) { + try { + return Integer.parseInt( + s.replaceAll("[^0-9]", "")); + } catch (Exception e) { + return 0; + } + } +} diff --git a/backup/src/main/java/Sdf1_login/UserGroupManager.java b/backup/src/main/java/Sdf1_login/UserGroupManager.java new file mode 100644 index 0000000..adaf907 --- /dev/null +++ b/backup/src/main/java/Sdf1_login/UserGroupManager.java @@ -0,0 +1,370 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.*; +import java.util.Base64; +import java.util.concurrent.ConcurrentHashMap; + +public class UserGroupManager { + + private final Main plugin; + private Connection db; + private final Map groupConfigs = new ConcurrentHashMap<>(); + + public UserGroupManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadGroupConfigs(); + startExpiryChecker(); + } + + // ===== DB ===== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(plugin.getDataFolder(), "usergroup.db"); + db = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_config (" + + "group_name TEXT PRIMARY KEY," + + "display_name TEXT DEFAULT ''," + + "display_color TEXT DEFAULT '§f'," + + "display_emoji TEXT DEFAULT ''," + + "priority INTEGER DEFAULT 0," + + "is_permanent INTEGER DEFAULT 1," + + "duration_minutes INTEGER DEFAULT 0," + + "pack_url TEXT DEFAULT ''," + + "pack_hash TEXT DEFAULT ''," + + "pack_prompt TEXT DEFAULT 'Custom Resource Pack'," + + "pack_size INTEGER DEFAULT 0)"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_member (" + + "player_name TEXT NOT NULL," + + "group_name TEXT NOT NULL," + + "added_by TEXT DEFAULT 'system'," + + "added_time INTEGER DEFAULT 0," + + "expiry_time INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, group_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException("[UserGroup] DB init failed: " + e.getMessage(), e); + } + } + + // ===== 配置加载 ===== + + public void loadGroupConfigs() { + groupConfigs.clear(); + File dir = new File(plugin.getDataFolder(), "用户组"); + if (!dir.exists()) dir.mkdirs(); + File[] files = dir.listFiles((d, n) -> n.endsWith(".txt")); + if (files == null) return; + + for (File f : files) { + try { + List lines = java.nio.file.Files.readAllLines( + f.toPath(), StandardCharsets.UTF_8); + UserGroupConfig cfg = parseGroupConfig(lines); + if (cfg != null) { + groupConfigs.put(cfg.name, cfg); + saveGroupConfigToDB(cfg); + } + } catch (Exception e) { + plugin.getLogger().warning("[UserGroup] Failed to load: " + f.getName()); + } + } + } + + private UserGroupConfig parseGroupConfig(List lines) { + UserGroupConfig cfg = new UserGroupConfig(); + boolean inBlock = false; + + for (String raw : lines) { + String l = raw; + + // 块注释 + if (l.contains("")) { inBlock = false; continue; } + if (l.contains("/*")) { inBlock = true; continue; } + if (l.contains("*/")) { inBlock = false; continue; } + if (inBlock) continue; + + // 行注释 + l = l.trim(); + if (l.isEmpty()) continue; + if (l.startsWith("#") || l.startsWith("//")) continue; + + // 行内注释 + int h = l.indexOf('#'); + int s = l.indexOf("//"); + if (h >= 0) l = l.substring(0, h).trim(); + if (s >= 0 && (h < 0 || s < h)) l = l.substring(0, s).trim(); + if (l.isEmpty()) continue; + + // 解析键值对 + String key = l; + String val = ""; + int idx = l.indexOf('\uff1a'); + if (idx < 0) idx = l.indexOf(':'); + if (idx >= 0) { + key = l.substring(0, idx).trim(); + val = l.substring(idx + 1).trim(); + if (val.startsWith("\"") && val.endsWith("\"")) + val = val.substring(1, val.length() - 1); + } + + switch (key) { + case "组名": + case "name": cfg.name = val; break; + case "显示名": + case "display": cfg.displayName = val; break; + case "颜色": + case "color": cfg.displayColor = val; break; + case "符号": + case "emoji": cfg.displayEmoji = val; break; + case "优先级": + case "priority": + try { cfg.priority = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "类型": + case "type": + cfg.permanent = val.equalsIgnoreCase("永久") || val.equalsIgnoreCase("permanent"); + break; + case "时长": + case "duration": + try { cfg.durationMinutes = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "资源包URL": + case "pack_url": cfg.packUrl = val; break; + case "资源包Hash": + case "pack_hash": cfg.packHash = val; break; + case "资源包提示": + case "pack_prompt": cfg.packPrompt = val; break; + case "资源包大小": + case "pack_size": + try { cfg.packSize = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "本地文件": + case "local_file": cfg.localFile = val; break; + } + } + + if (cfg.name == null || cfg.name.isEmpty()) return null; + return cfg; + } + + private void saveGroupConfigToDB(UserGroupConfig cfg) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_config " + + "(group_name, display_name, display_color, display_emoji," + + " priority, is_permanent, duration_minutes," + + " pack_url, pack_hash, pack_prompt, pack_size)" + + " VALUES (?,?,?,?,?,?,?,?,?,?,?)"); + ps.setString(1, cfg.name); + ps.setString(2, cfg.displayName); + ps.setString(3, cfg.displayColor); + ps.setString(4, cfg.displayEmoji); + ps.setInt(5, cfg.priority); + ps.setInt(6, cfg.permanent ? 1 : 0); + ps.setInt(7, cfg.durationMinutes); + ps.setString(8, cfg.packUrl); + ps.setString(9, cfg.packHash); + ps.setString(10, cfg.packPrompt); + ps.setInt(11, cfg.packSize); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 玩家操作 ===== + + public boolean addPlayer(String player, String groupName, String addedBy, long durationMinutes) { + UserGroupConfig cfg = groupConfigs.get(groupName); + if (cfg == null) return false; + long now = System.currentTimeMillis(); + long expiry = cfg.permanent ? 0 : now + durationMinutes * 60 * 1000; + if (!cfg.permanent && durationMinutes > 0) expiry = now + durationMinutes * 60 * 1000; + else if (!cfg.permanent) expiry = now + cfg.durationMinutes * 60 * 1000; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_member " + + "(player_name, group_name, added_by, added_time, expiry_time)" + + " VALUES (?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, groupName); + ps.setString(3, addedBy); + ps.setLong(4, now); + ps.setLong(5, expiry); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + Player p = plugin.getServer().getPlayerExact(player); + if (p != null) sendResourcePack(p); + return true; + } + + public boolean removePlayer(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + int rows = ps.executeUpdate(); + ps.close(); + return rows > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public List> getPlayerGroups(String player) { + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM user_group_member WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + long now = System.currentTimeMillis(); + while (rs.next()) { + Map row = new LinkedHashMap<>(); + row.put("group_name", rs.getString("group_name")); + row.put("added_by", rs.getString("added_by")); + long expiry = rs.getLong("expiry_time"); + row.put("expiry_time", expiry); + row.put("expired", expiry > 0 && expiry <= now); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public UserGroupConfig getHighestGroup(String player) { + List> groups = getPlayerGroups(player); + long now = System.currentTimeMillis(); + UserGroupConfig best = null; + for (Map g : groups) { + long expiry = (long) g.get("expiry_time"); + if (expiry > 0 && expiry <= now) continue; + String name = (String) g.get("group_name"); + UserGroupConfig cfg = groupConfigs.get(name); + if (cfg != null && (best == null || cfg.priority > best.priority)) { + best = cfg; + } + } + return best; + } + + public boolean isPlayerInGroup(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + boolean exists = ps.executeQuery().next(); + ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ===== 过期自动移除 ===== + + private void startExpiryChecker() { + new BukkitRunnable() { + @Override + public void run() { + checkExpiry(); + } + }.runTaskTimerAsynchronously(plugin, 1200L, 1200L); // 每分钟检查 + } + + private void checkExpiry() { + try { + long now = System.currentTimeMillis(); + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, group_name FROM user_group_member " + + "WHERE expiry_time > 0 AND expiry_time <= ?"); + ps.setLong(1, now); + ResultSet rs = ps.executeQuery(); + List expired = new ArrayList<>(); + while (rs.next()) { + expired.add(new String[]{ + rs.getString("player_name"), + rs.getString("group_name")}); + } + rs.close(); ps.close(); + + for (String[] e : expired) { + PreparedStatement del = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + del.setString(1, e[0]); + del.setString(2, e[1]); + del.executeUpdate(); + del.close(); + plugin.getLogger().info("[UserGroup] 过期移除: " + e[0] + " -> " + e[1]); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 资源包分发 ===== + + public void sendResourcePack(Player p) { + UserGroupConfig cfg = getHighestGroup(p.getName()); + if (cfg == null || cfg.packUrl == null || cfg.packUrl.isEmpty()) return; + + String url = cfg.packUrl; + String prompt = cfg.packPrompt != null + ? cfg.packPrompt : "Custom Resource Pack"; + + plugin.getServer().getScheduler().runTaskLater(plugin, () -> { + try { + p.setResourcePack(url, new byte[0], prompt, false); + plugin.getLogger().info("[UserGroup] 资源包已发送: " + + p.getName() + " -> " + cfg.displayName); + } catch (Exception e) { + plugin.getLogger().warning( + "[UserGroup] 资源包发送失败: " + e.getMessage()); + } + }, 40L); + } + + public Map getGroupConfigs() { + return groupConfigs; + } + + // ===== 配置类 ===== + + public static class UserGroupConfig { + public String name; + public String displayName = ""; + public String displayColor = "§f"; + public String displayEmoji = ""; + public int priority = 0; + public boolean permanent = true; + public int durationMinutes = 0; + public String packUrl = ""; + public String packHash = ""; + public String packPrompt = "Custom Resource Pack"; + public int packSize = 0; + public String localFile = ""; + } +} diff --git a/backup/src/main/java/Sdf1_login/VerificationManager.java b/backup/src/main/java/Sdf1_login/VerificationManager.java new file mode 100644 index 0000000..3fc4ceb --- /dev/null +++ b/backup/src/main/java/Sdf1_login/VerificationManager.java @@ -0,0 +1,158 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.function.Consumer; + +public class VerificationManager { + + private final Main plugin; + + public VerificationManager(Main plugin) { + this.plugin = plugin; + } + + public boolean isBedrockPlayer(Player p) { + String uuid = p.getUniqueId().toString(); + return uuid.contains("f851ba4") + || uuid.startsWith("00000000-0000-0000"); + } + + public boolean isOnlineMode() { + return Bukkit.getServer().getOnlineMode(); + } + + public void verifyPremiumAsync(Player player, + Consumer callback) { + Thread t = new Thread(() -> doVerify(player, callback), + "Sdf1_login-PremiumVerify"); + t.setDaemon(true); + t.start(); + } + + private void doVerify(Player player, + Consumer callback) { + String name = player.getName(); + String uuid = player.getUniqueId().toString(); + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证开始 ======"); + plugin.getLogger().info( + "[Sdf1_login] 玩家: " + name); + plugin.getLogger().info( + "[Sdf1_login] 当前UUID: " + uuid); + + try { + UUID offlineUuid = UUID.nameUUIDFromBytes( + ("OfflinePlayer:" + name) + .getBytes(StandardCharsets.UTF_8)); + String offlineStr = offlineUuid.toString(); + + plugin.getLogger().info( + "[Sdf1_login] 离线UUID: " + offlineStr); + + boolean isOfflineUuid = + player.getUniqueId().equals(offlineUuid); + + plugin.getLogger().info( + "[Sdf1_login] UUID匹配离线格式: " + + isOfflineUuid); + + if (!isOfflineUuid) { + plugin.getLogger().info( + "[Sdf1_login] 结论: 正版玩家"); + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(true); + return; + } + + plugin.getLogger().info( + "[Sdf1_login] UUID为离线格式," + + "查询Mojang API..."); + UUID mojangUuid = fetchMojangUuid(name); + + if (mojangUuid != null) { + plugin.getLogger().info( + "[Sdf1_login] Mojang返回UUID: " + + mojangUuid); + plugin.getLogger().info( + "[Sdf1_login] 结论: 盗版客户端" + + "使用了正版名字"); + } else { + plugin.getLogger().info( + "[Sdf1_login] Mojang无此名字"); + plugin.getLogger().info( + "[Sdf1_login] 结论: 纯离线玩家"); + } + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(false); + + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 验证异常: " + + e.getMessage()); + callback.accept(false); + } + } + + private UUID fetchMojangUuid(String name) { + HttpURLConnection conn = null; + try { + URL url = new URL( + "https://api.mojang.com/users/profiles/minecraft/" + + name); + conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + conn.setRequestMethod("GET"); + + int code = conn.getResponseCode(); + plugin.getLogger().info( + "[Sdf1_login] Mojang API响应: " + code); + + if (code == 200) { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn.getInputStream(), + StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + + String json = sb.toString(); + String uuidStr = json.replaceAll( + ".*\"id\"\\s*:\\s*\"([^\"]+)\".*", + "$1"); + plugin.getLogger().info( + "[Sdf1_login] Mojang原始UUID: " + + uuidStr); + + String formatted = uuidStr.replaceFirst( + "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", + "$1-$2-$3-$4-$5"); + return UUID.fromString(formatted); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] Mojang查询异常: " + + e.getMessage()); + } finally { + if (conn != null) conn.disconnect(); + } + return null; + } +} diff --git a/backup/src/main/java/Sdf1_login/WelcomeManager.java b/backup/src/main/java/Sdf1_login/WelcomeManager.java new file mode 100644 index 0000000..68bd3ef --- /dev/null +++ b/backup/src/main/java/Sdf1_login/WelcomeManager.java @@ -0,0 +1,163 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class WelcomeManager { + + private final Main plugin; + private final Random random = new Random(); + private final List registerMsgs = + new ArrayList<>(); + private final List loginMsgs = + new ArrayList<>(); + + public WelcomeManager(Main plugin) { + this.plugin = plugin; + loadMessages(); + } + + public void loadMessages() { + registerMsgs.clear(); + loginMsgs.clear(); + File f = new File( + plugin.getDataFolder(), + "欢迎仪式.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("注册") + || t.equals("注册:")) { + section = "register"; + continue; + } + if (t.equals("登录") + || t.equals("登录:")) { + section = "login"; + continue; + } + // 支持 = 和 : 两种分隔 + if (section.isEmpty()) { + if (t.startsWith("注册") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + registerMsgs.add( + colorize(v)); + continue; + } + if (t.startsWith("登录") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + loginMsgs.add( + colorize(v)); + continue; + } + continue; + } + if (!t.isEmpty()) { + if (section.equals("register")) + registerMsgs.add( + colorize(t)); + else + loginMsgs.add( + colorize(t)); + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[欢迎] 加载失败: " + + e.getMessage()); + } + plugin.getLogger().info( + "[欢迎] 加载完成: 注册=" + + registerMsgs.size() + + " 登录=" + loginMsgs.size()); + } + + public void onRegister(Player p) { + sendRandom(p, registerMsgs, "注册"); + } + + public void onLogin(Player p) { + sendRandom(p, loginMsgs, "登录"); + } + + private void sendRandom(Player p, + List msgs, String tag) { + if (msgs.isEmpty()) return; + String msg = msgs.get( + random.nextInt(msgs.size())); + msg = msg.replace("{player}", + p.getName()); + final String finalMsg = msg; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> Bukkit + .broadcastMessage( + finalMsg), 10L); + } + + private String colorize(String msg) { + return msg.replace("&", "\u00a7"); + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets + .UTF_8))) { + pw.println("# 欢迎仪式配置"); + pw.println("# &为颜色代码"); + pw.println("# {player} = 玩家名"); + pw.println(); + pw.println("注册:"); + pw.println("&a&l欢迎 &f{player} &a加入服务器!&7[注册]"); + pw.println("&6&l{player} &e已完成注册,欢迎入驻!&7[注册]"); + pw.println("&b&l新成员 &f{player} &b已就位!&7[注册]"); + pw.println(); + pw.println("登录:"); + pw.println("&a&l欢迎回来 &f{player}&a!&7[登录]"); + pw.println("&e&l{player} &e已登录,继续冒险吧!&7[登录]"); + pw.println("&b&l{player} &b回来了!&7[登录]"); + } catch (IOException ignored) { + } + } +} diff --git a/backup/src/main/resources/plugin.yml b/backup/src/main/resources/plugin.yml new file mode 100644 index 0000000..f6868d9 --- /dev/null +++ b/backup/src/main/resources/plugin.yml @@ -0,0 +1,104 @@ +name: Sdf1_login +version: 1.0 +main: Sdf1_login.Main +api-version: '1.21' +commands: + sdf1_login: + description: Sdf1_login主命令 + usage: /sdf1_login + reg: + description: 注册 + usage: /reg <密码> + login: + description: 登录 + usage: /login <密码> + l: + description: 登录快捷版 + usage: /l <密码> + 注册: + description: 注册(中文) + usage: /注册 <密码> + 登录: + description: 登录(中文) + usage: /登录 <密码> + 玩家信息: + description: 打开面板(中文) + usage: /玩家信息 + 改密码: + description: 修改密码(中文) + usage: /改密码 <旧密码> <新密码> <确认新密码> + 设置邮箱: + description: 设置邮箱(中文) + usage: /设置邮箱 <邮箱地址> + 撤销: + description: 撤销操作(中文) + usage: /撤销 + 删除: + description: 删除玩家(中文) + usage: /删除 <玩家> <密码> + 找回密码: + description: 找回密码(中文) + usage: /找回密码 + 签到: + description: 每日签到(中文) + usage: /签到 + #oa: + # description: 申请删除账号 + # usage: /oa [玩家名] + 删除账号: + description: 申请删除账号(中文) + usage: /删除账号 [玩家名] + 通过: + description: 批准OA请求(中文) + usage: /通过 <编号> + 拒绝: + description: 拒绝OA请求(中文) + usage: /拒绝 <编号> [理由] + accept: + description: 批准OA请求 + usage: /accept <编号> + deny: + description: 拒绝OA请求 + usage: /deny <编号> [理由] + email: + description: 绑定邮箱 + usage: /email <邮箱> + 绑定: + description: 绑定邮箱(中文) + usage: /绑定 <邮箱> + 绑定邮箱: + description: 绑定邮箱 + usage: /绑定邮箱 + chat: + description: 聊天过滤管理 + usage: /chat + recycle: + description: 打开垃圾回收站 + usage: /recycle + 垃圾清理: + description: 手动清理掉落物 + usage: /垃圾清理 + menu: + description: 雪球菜单开关控制 + usage: /menu <开/关> + pvp : + description: PVP榜单 + usage: /pvp + cypay : + description: 草原探险债券 + usage: /cypay + printer : + description: 打印债券流水 + usage: /printer + shop : + description: 游戏商人 + usage: /shop + 商店: + description: 打开商店 + usage: /商店 + #testorder: + # description: 测试订单中心 + # usage: /testorder + protect: + description: 区域保护 + usage: /protect \ No newline at end of file diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/AFKManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AFKManager.java new file mode 100644 index 0000000..f714c1b --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AFKManager.java @@ -0,0 +1,197 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +public class AFKManager { + + // 玩家最后活动时间(空闲才开始计时) + private final Map lastAction = + new HashMap<>(); + // 每个玩家当前的警告阶段 + private final Map lastStage = + new HashMap<>(); + // 是否已进入挂机检测(false=还在活动,不计时) + private final Map afkTracking = + new HashMap<>(); + + private final ConfigManager config; + private final JavaPlugin plugin; + + public AFKManager(ConfigManager config, + JavaPlugin plugin) { + this.config = config; + this.plugin = plugin; + } + + public void startCheck() { + new BukkitRunnable() { + public void run() { + tick(); + } + }.runTaskTimer(plugin, 20L, 20L); + } + + private void tick() { + if (!config.afkEnabled) return; + long now = System.currentTimeMillis(); + + for (Player p : Bukkit.getOnlinePlayers()) { + String name = p.getName(); + UUID uuid = p.getUniqueId(); + + if (!isLoggedIn(name)) continue; + if (config.getAfkWhitelist().contains(name)) + continue; + + // 确保有记录 + if (!lastAction.containsKey(uuid)) { + lastAction.put(uuid, now); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + long last = lastAction.get(uuid); + long idle = (now - last) / 1000; + int timeout = config.afkTimeout; + + // 还在活动范围内:不进入挂机检测 + if (idle < 60) { + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + continue; + } + + // 超过60秒无活动:开始挂机检测 + afkTracking.put(uuid, true); + long remain = timeout - idle; + + // 已超时:踢出 + if (remain <= 0) { + p.kickPlayer("§c§l你已挂机 " + + fmtSec(idle) + ",被踢出"); + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + continue; + } + + int stage = lastStage.getOrDefault(uuid, 0); + + // 阶段3:最后10秒 + if (remain <= 10) { + if (stage < 3) { + lastStage.put(uuid, 3); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + remain + " 秒踢出", + 0, 22, 3); + playSound(p); + continue; + } + + // 阶段2:1分钟~10秒 + if (remain <= 60) { + if (stage < 2) { + lastStage.put(uuid, 2); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + } + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§c还剩 " + fmtSec(remain) + " 踢出", + 5, 25, 5); + playSound(p); + continue; + } + + // 阶段1:3分钟提醒 + if (remain <= 180 && stage < 1) { + lastStage.put(uuid, 1); + p.sendMessage( + "§e§l⚠ 挂机警告 ⚠ §f你已挂机 §c" + + fmtSec(idle) + + "§f,还剩 §c" + + fmtSec(remain) + + "§f踢出"); + p.sendTitle( + "§e§l挂机了 " + fmtSec(idle), + "§f还剩 " + fmtSec(remain) + " 踢出", + 10, 60, 20); + for (int i = 0; i < 3; i++) { + final int delay = i * 10; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + if (p.isOnline()) playSound(p); + }, delay); + } + } + + if (remain > 180) { + lastStage.remove(uuid); + } + } + } + + /** + * 玩家有活动时调用(移动/交互/放置/挖掘等) + * 重置计时,退出挂机检测 + */ + public void recordAction(UUID uuid) { + lastAction.put(uuid, System.currentTimeMillis()); + afkTracking.put(uuid, false); + lastStage.put(uuid, 0); + } + + /** + * 玩家下线时调用 + * 彻底停止计时,清理所有数据 + */ + public void remove(UUID uuid) { + lastAction.remove(uuid); + lastStage.remove(uuid); + afkTracking.remove(uuid); + } + + /** + * 获取玩家当前是否在挂机检测中 + */ + public boolean isAfkTracking(UUID uuid) { + return afkTracking.getOrDefault(uuid, false); + } + + private boolean isLoggedIn(String name) { + return ((Main) plugin).getLoggedIn() + .contains(name); + } + + private void playSound(Player p) { + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound.BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + + private String fmtSec(long sec) { + if (sec < 60) return sec + "秒"; + long m = sec / 60; + long s = sec % 60; + return m + "分" + (s > 0 ? s + "秒" : ""); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/AccountRequestManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AccountRequestManager.java new file mode 100644 index 0000000..ae5eaad --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AccountRequestManager.java @@ -0,0 +1,146 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class AccountRequestManager { + + public enum ApprovalMode { AUTO, MANUAL, HYBRID } + + public static class Request { + public final int id; + public final String requester; + public final String target; + public final String ip; + public final long time; + public String status; + public String denyReason; + + public Request(int id, String requester, String target, String ip) { + this.id = id; + this.requester = requester; + this.target = target; + this.ip = ip; + this.time = System.currentTimeMillis(); + this.status = "PENDING"; + this.denyReason = ""; + } + } + + private final Main plugin; + private final Map requests = new ConcurrentHashMap<>(); + private final Map pendingByPlayer = new ConcurrentHashMap<>(); + private int nextId = 1; + private ApprovalMode mode = ApprovalMode.MANUAL; + private int autoDelayMinutes = 60; + + public AccountRequestManager(Main plugin) { + this.plugin = plugin; + } + + public ApprovalMode getMode() { return mode; } + + public void setMode(String m) { + try { this.mode = ApprovalMode.valueOf(m.toUpperCase()); } + catch (Exception e) { this.mode = ApprovalMode.MANUAL; } + } + + public void setAutoDelayMinutes(int min) { + this.autoDelayMinutes = min; + } + + public Request createRequest(String requester, String target, String ip) { + int id = nextId++; + Request req = new Request(id, requester, target, ip); + requests.put(id, req); + pendingByPlayer.put(requester, id); + + if (mode == ApprovalMode.AUTO) { + scheduleAutoApprove(id); + } else if (mode == ApprovalMode.HYBRID) { + String targetIp = (String) plugin.getDb().getField(target, "ip_address"); + if (ip != null && ip.equals(targetIp)) { + scheduleAutoApprove(id); + } + } + return req; + } + + private void scheduleAutoApprove(int id) { + long delayTicks = (long) autoDelayMinutes * 60L * 20L; + Request req = requests.get(id); + if (req == null) return; + String requesterName = req.requester; + String targetName = req.target; + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 将在 " + autoDelayMinutes + " 分钟后自动通过"); + + Bukkit.getScheduler().runTaskLater(plugin, () -> { + Request r = requests.get(id); + if (r != null && "PENDING".equals(r.status)) { + approveRequest(id); + plugin.getLogger().info("[Sdf1_login] OA#" + id + " 自动通过"); + Player requester = Bukkit.getPlayer(requesterName); + if (requester != null && requester.isOnline()) { + requester.sendMessage("§a[Sdf1_login] §f您的删除请求 #" + id + "(§e" + targetName + "§a)已自动通过!"); + } + } + }, delayTicks); + } + + public boolean approveRequest(int id) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "APPROVED"; + plugin.getDb().deleteUser(req.target); + pendingByPlayer.remove(req.requester); + // 清背包+传送出生点+踢出 + Player target = Bukkit.getPlayer(req.target); + if (target != null && target.isOnline()) { + target.getInventory().clear(); + target.getInventory().setArmorContents(new org.bukkit.inventory.ItemStack[4]); + target.getInventory().setExtraContents(new org.bukkit.inventory.ItemStack[1]); + target.teleport(target.getWorld().getSpawnLocation()); + target.kickPlayer("§c§l你的账号已被管理员删除,请重新注册"); + } + return true; + } + + + public boolean denyRequest(int id, String reason) { + Request req = requests.get(id); + if (req == null || !"PENDING".equals(req.status)) return false; + req.status = "DENIED"; + req.denyReason = reason; + pendingByPlayer.remove(req.requester); + return true; + } + + public List getPendingRequests() { + List list = new ArrayList<>(); + for (Request r : requests.values()) { + if ("PENDING".equals(r.status)) list.add(r); + } + return list; + } + + public Request getRequest(int id) { + return requests.get(id); + } + + public boolean hasPending(String player) { + return pendingByPlayer.containsKey(player); + } + + public void cancelPending(String player) { + Integer id = pendingByPlayer.remove(player); + if (id != null) { + Request r = requests.get(id); + if (r != null) r.status = "CANCELLED"; + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/AreaProtection.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AreaProtection.java new file mode 100644 index 0000000..bc53d53 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/AreaProtection.java @@ -0,0 +1,4907 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockIgniteEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import java.sql.*; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.GameMode; +import org.bukkit.event.EventPriority; + + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import java.util.Iterator; +import java.util.Set; +import java.util.HashSet; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.entity.Item; + + + + +public class AreaProtection implements Listener { + + private final Main plugin; + public List giveEffects = new ArrayList<>(); + private final Map> playerAreas + = new HashMap<>(); + private final Map areas = + new ConcurrentHashMap<>(); + private Connection dbConnection; + + // 全局白名单(玩家) + private final Set globalPlayerWhitelist = + ConcurrentHashMap.newKeySet(); + // 区域白名单 key=区域名 value=玩家名集合 + private final Map> areaPlayerWhitelist = + new ConcurrentHashMap<>(); + // 全局黑名单(物品ID) + private final Set globalItemBlacklist = + ConcurrentHashMap.newKeySet(); + // 区域黑名单 + private final Map> areaItemBlacklist = + new ConcurrentHashMap<>(); + + // 选地 + private static final Material WAND = Material.BREEZE_ROD; + + private final Map pos1 = + new ConcurrentHashMap<>(); + private final Map pos2 = + new ConcurrentHashMap<>(); + private final Set selecting = + ConcurrentHashMap.newKeySet(); + public boolean denyRaid = false; + // ===== 统一文件夹 ===== + private final File rootDir; + private final File whitelistDir; + private final File globalWhitelistFile; + private final File oldDbFile; + private final File newDbFile; + + // 边框显示 + private final Map displayTaskIds = + new ConcurrentHashMap<>(); + private final Map> globalWhitelist + = new HashMap<>(); + private final Map> areaWhitelist + = new HashMap<>(); + private final Map protectedEntities + = new ConcurrentHashMap<>(); + // 区域给予的效果记录(贴标) + private final Map> playerAppliedEffects = new HashMap<>(); + // 延时清理任务 + private static final long MOVE_DEBOUNCE_MS = 100; + // 新:全部改为线程安全 + private final Map playerCurrentArea + = new ConcurrentHashMap<>(); + private final Map> playerMarkedEffects + = new ConcurrentHashMap<>(); + private final Map pendingClearTask + = new ConcurrentHashMap<>(); + private final Map pendingClearArea + = new ConcurrentHashMap<>(); + private final Map lastMoveProcess + = new ConcurrentHashMap<>(); + // 防止传送时重复处理 + private final Set teleporting + = ConcurrentHashMap.newKeySet(); + // 待清理的效果列表(与延时任务配合) + private final Map> pendingClearEffects + = new ConcurrentHashMap<>(); + + + +// ==================== 强制游戏模式 ==================== + + /** + * 监听玩家尝试切换游戏模式 + * 如果在强制游戏模式区域且不在豁免名单中,则取消切换 + */ + // 防止递归的标记 + private final Set modeSwitching + = ConcurrentHashMap.newKeySet(); + @EventHandler(priority = EventPriority.HIGHEST) + public void onGameModeChange( + PlayerGameModeChangeEvent event) { + if (event.isCancelled()) return; + + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + if (modeSwitching.contains(uid)) return; + + String areaName = playerCurrentArea.get(uid); + if (areaName == null) return; + + AreaConfig ac = areas.get(areaName); + if (ac == null) return; + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) return; + if (isExemptFromGameMode(p, areaName)) return; + + event.setCancelled(true); + + modeSwitching.add(uid); + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } finally { + modeSwitching.remove(uid); + } + + // ★ 切换后发一次中文提示 + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + ac.enforceGameMode + + " 模式,无法切换")); + } + + public Set getAlreadyForced() { + return alreadyForced; + } + + + // ==================== 定时强制游戏模式 ==================== + + private BukkitTask enforceTask; + + /** + * 启动定时强制任务 + * 每秒检查一次所有在线玩家 + */ + private BukkitTask enforceTask1; + + // 记录上次强制模式的玩家(避免重复发消息) + private final Set alreadyForced + = ConcurrentHashMap.newKeySet(); + + public void startEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + } + enforceTask = new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String areaName = + playerCurrentArea.get(uid); + if (areaName == null) { + // 不在任何区域,清除标记 + alreadyForced.remove(uid); + continue; + } + + AreaConfig ac = areas.get(areaName); + if (ac == null) continue; + if (ac.enforceGameMode == null + || ac.enforceGameMode + .isEmpty()) continue; + if (isExemptFromGameMode( + p, areaName)) { + alreadyForced.remove(uid); + continue; + } + + GameMode target = + GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + modeSwitching.add(uid); + try { + p.setGameMode(target); + } finally { + modeSwitching.remove(uid); + } + + // ★ 只在玩家已被强制过时才发消息 + // 首次不发,后续才提醒 + if (alreadyForced.contains(uid)) { + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + + ac.enforceGameMode + + " 模式,无法切换")); + } + alreadyForced.add(uid); + } else { + // 模式一致,清除标记 + alreadyForced.remove(uid); + } + } + } + }.runTaskTimer(plugin, 20L, 20L); + } + + public void stopEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + enforceTask = null; + } + } + + + + /** + * 检查玩家是否豁免于游戏模式强制 + * 豁免条件:全局白名单 / 区域白名单 / 模式排除名单 + */ + private boolean isExemptFromGameMode( + Player p, String areaName) { + String name = p.getName(); + + /* if (p.isOp()) { + plugin.getLogger().info( + "[防护-调试] 豁免原因: OP, 玩家=" + name); + return true; + }*/ + if (globalPlayerWhitelist.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 全局白名单, 玩家=" + + name);*/ + return true; + } + Set aw = + areaPlayerWhitelist.get(areaName); + if (aw != null && aw.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 区域白名单, 玩家=" + + name + " 区域=" + areaName);*/ + return true; + } + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 模式排除名单, 玩家=" + + name);*/ + return true; + } + /* plugin.getLogger().info( + "[防护-调试] 无豁免, 玩家=" + name);*/ + return false; + } + + /** + * 玩家进入区域时,如果该区域强制游戏模式, + * 则立即设置(排除豁免玩家) + */ + private void applyEnforcedGameMode(Player p, + String areaName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: ac=null, area=" + + areaName);*/ + return; + } + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: 未配置强制模式, area=" + + areaName);*/ + return; + } + + boolean exempt = isExemptFromGameMode( + p, areaName); + /* plugin.getLogger().info( + "[防护-调试] applyMode: 玩家=" + p.getName() + + " 区域=" + areaName + + " 强制=" + ac.enforceGameMode + + " 当前=" + p.getGameMode() + + " 豁免=" + exempt + + " OP=" + p.isOp() + + " 全白=" + globalPlayerWhitelist + .contains(p.getName()));*/ + + if (exempt) return; + + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + /* plugin.getLogger().info( + "[防护] 强制切换 " + p.getName() + + " → " + ac.enforceGameMode);*/ + } + } catch (Exception e) { + /* plugin.getLogger().warning( + "[防护] 强制模式失败: " + + e.getMessage());*/ + } + } + + public AreaProtection(Main plugin) { + this.plugin = plugin; + + // 统一根目录 + rootDir = new File(plugin.getDataFolder(), "区域防护"); + if (!rootDir.exists()) rootDir.mkdirs(); + + // 白名单子目录 + whitelistDir = new File(rootDir, "whitelists"); + if (!whitelistDir.exists()) whitelistDir.mkdirs(); + globalWhitelistFile = new File( + whitelistDir, "全局白名单.txt"); + + // 数据库迁移 + oldDbFile = new File( + plugin.getDataFolder(), "area_effects.db"); + newDbFile = new File(rootDir, "area_effects.db"); + migrateDatabase(); + + // 初始化 + initDatabase(); + writeDefaultConfig(); + loadAllAreas(); + loadWhitelists(); + recoverPendingEffects(); + } + public void recoverPendingEffects() { + try { + // 1. 遍历所有在线玩家(而不是遍历DB记录) + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 2. 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + // 3. 获取该玩家在DB中的所有历史记录 + List recordedAreas = + getPlayerAreas(uid); + + // 4. 清理不在当前区域的旧记录 + for (String recordedArea : recordedAreas) { + if (currentAc == null + || !currentAc.name + .equals(recordedArea)) { + // 玩家不在这个区域了 → 清DB + removePlayerEffects( + uid, recordedArea); + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在" + recordedArea + + ", 清理旧记录"); + } + } + + // 5. 如果在区域中 → 恢复/给予效果 + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } else { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在任何区域, 跳过"); + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 恢复效果失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + // 取消延时清理任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 清标记 + playerMarkedEffects.remove(uid); + playerCurrentArea.remove(uid); + } + + +// ==================== 数据库迁移 ==================== + + /** + * 如果旧位置有 db 文件,移到新位置 + * 如果新位置已有,不动 + * 如果旧位置也没有,在新位置创建 + */ + + private void migrateDatabase() { + if (newDbFile.exists()) { + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + oldDbFile.delete(); + } + return; + } + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + try { + java.nio.file.Files.move( + oldDbFile.toPath(), + newDbFile.toPath(), + java.nio.file.StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 迁移数据库失败: " + + e.getMessage()); + } + } + } + + + // 保存玩家进入区域时给予的效果 + private void savePlayerEffects(UUID uid, String playerName, + String areaName, List effects) { + try { + // 先删除该区域旧记录,避免重复 + PreparedStatement del = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + del.setString(1, uid.toString()); + del.setString(2, areaName); + del.executeUpdate(); + del.close(); + + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)"); + long now = System.currentTimeMillis(); + for (PotionEffectType type : effects) { + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, type.getName()); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, now); + ps.addBatch(); + } + ps.executeBatch(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 保存效果失败: " + e.getMessage()); + } + } + + // 查询玩家在某个区域获得的效果 + private List getPlayerEffects( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + PotionEffectType type = PotionEffectType + .getByName(rs.getString("effect_type")); + if (type != null) list.add(type); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + // 删除玩家在某个区域的效果记录 + private void removePlayerEffects(UUID uid, String areaName) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ? AND area_name = ?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除效果失败: " + e.getMessage()); + } + } + + // 删除玩家所有效果记录 + private void removeAllPlayerEffects(UUID uid) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "DELETE FROM player_effects " + + "WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 删除全部效果失败: " + e.getMessage()); + } + } + + // 获取玩家所有记录的区域 + private List getPlayerAreas(UUID uid) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT DISTINCT area_name " + + "FROM player_effects WHERE uuid = ?"); + ps.setString(1, uid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("area_name")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询区域失败: " + e.getMessage()); + } + return list; + } + + // 查询所有待处理的效果(跨服重启也能恢复) + private List getAllPendingEffects() { + List list = new ArrayList<>(); + try { + Statement stmt = dbConnection.createStatement(); + ResultSet rs = stmt.executeQuery( + "SELECT uuid, player_name, area_name, " + + "effect_type FROM player_effects"); + while (rs.next()) { + list.add(new String[]{ + rs.getString("uuid"), + rs.getString("player_name"), + rs.getString("area_name"), + rs.getString("effect_type")}); + } + rs.close(); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询全部效果失败: " + e.getMessage()); + } + return list; + } + + + private void writeDefaultConfig() { + File f = new File(rootDir, "末地保护区.txt"); + if (f.exists()) return; + + String endWorld = "world_the_end"; + for (org.bukkit.World w : Bukkit.getWorlds()) { + if (w.getEnvironment() + == org.bukkit.World.Environment.THE_END) { + endWorld = w.getName(); + break; + } + } + + final String ew = endWorld; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 末地保护区"); + pw.println("起点: " + ew + ",-30000000,-30000000"); + pw.println("终点: " + ew + ",30000000,30000000"); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("没收物品: BROWN_MUSHROOM,RED_MUSHROOM,MUSHROOM_STEW"); + pw.println("没收提示: 你携带了违禁品「蘑菇」,已被全部没收!"); + pw.println("通报批评: {player} 在末地违规携带蘑菇!"); + pw.println(); + pw.println("禁止放置方块"); + pw.println("禁止破坏方块"); + pw.println("禁止PVP"); + pw.println("禁止袭击"); + pw.println(); + pw.println("效果: 夜视 1 999"); + pw.println("进入提示: 末地保护区 - 请遵守规则"); + pw.println("离开提示: 已离开末地保护区"); + pw.close(); + plugin.getLogger().info("[防护] 生成末地配置, 世界: " + ew); + } catch (IOException ignored) { + } + } + + public void reload() { + loadAllAreas(); + loadWhitelists(); + // 强制输出醒目的中文日志 + plugin.getLogger().info("========================================"); + plugin.getLogger().info("[防护] 已重载 " + areas.size() + " 个区域"); + plugin.getLogger().info("[防护] 已重载白名单"); + plugin.getLogger().info("========================================"); + } + +// ==================== 消息格式化 ==================== + + /** + * 格式化区域消息 + * 1. & → §(颜色符号转换) + * 2.
    和 \n → 换行 + * 3. 内容 → §l内容§r(加粗) + * 4. 自动添加 [区域防护] 前缀 + */ + private String formatAreaMsg(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + String msg = raw; + + // & → § + for (int i = 0; i <= 9; i++) { + msg = msg.replace("&" + i, "§" + i); + } + for (char c : "abcdefklnor".toCharArray()) { + msg = msg.replace("&" + c, "§" + c); + } + + // 换行 + msg = msg.replace("
    ", "\n"); + msg = msg.replace("\\n", "\n"); + + // HTML → Minecraft格式(栈式解析,支持嵌套) + msg = convertHtmlTags(msg); + + // 前缀 + msg = "§c§l【区域防护】§r " + msg; + + return msg; + } + + /** + * 栈式HTML标签解析 + * 处理 text rest 这样的嵌套标签 + * 内层关闭时重新应用外层格式 + */ + private String convertHtmlTags(String msg) { + StringBuilder sb = new StringBuilder(); + java.util.Deque stack = + new ArrayDeque<>(); + int i = 0; + + while (i < msg.length()) { + if (msg.charAt(i) == '<' + && i + 1 < msg.length()) { + if (msg.charAt(i + 1) == '/') { + // 关闭标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 2, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0 && !stack.isEmpty() + && stack.peek() == code) { + stack.pop(); + if (!stack.isEmpty()) { + // 重置后重新应用外层格式 + sb.append("§r"); + for (char c : stack) { + sb.append("§").append(c); + } + } else { + sb.append("§r"); + } + } + i = end + 1; + } else { + // 开放标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 1, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0) { + stack.push(code); + sb.append("§").append(code); + } + i = end + 1; + } + } else { + sb.append(msg.charAt(i)); + i++; + } + } + return sb.toString(); + } + + private char htmlToMc(String tag) { + switch (tag) { + case "b": return 'l'; + case "i": return 'o'; + case "u": return 'n'; + case "s": return 'm'; + default: return 0; + } + } + +// =============== 加载 ==================== + + public void loadAllAreas() { + areas.clear(); + File[] files = rootDir.listFiles( + (File d, String n) -> n.endsWith(".txt")); + if (files == null) return; + for (File f : files) { + try { + String name = f.getName() + .replace(".txt", ""); + AreaConfig ac = parseArea(name, f); + areas.put(name, ac); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载失败: " + f.getName()); + } + } + } + + + private void initDatabase() { + try { + dbConnection = DriverManager.getConnection( + "jdbc:sqlite:" + newDbFile.getAbsolutePath()); + Statement stmt = dbConnection.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS player_effects (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "uuid TEXT NOT NULL," + + "player_name TEXT," + + "area_name TEXT NOT NULL," + + "effect_type TEXT NOT NULL," + + "effect_level INTEGER DEFAULT 0," + + "effect_duration INTEGER DEFAULT 999," + + "enter_time INTEGER NOT NULL)"); + stmt.executeUpdate( + "CREATE INDEX IF NOT EXISTS idx_uuid " + + "ON player_effects(uuid)"); + stmt.close(); + } catch (SQLException e) { + plugin.getLogger().severe( + "[防护] 数据库初始化失败: " + + e.getMessage()); + } + } + + +// ==================== 效果名称中英文映射 ==================== + + /** + * 将中文效果名转为 Bukkit 能识别的英文名 + */ + private String resolveEffectName(String name) { + if (name == null) return null; + String n = name.trim(); + // 如果已经是英文或命名空间格式,直接用 + if (n.contains(":") + || n.equals(n.toUpperCase())) { + return n; + } + // 中文 → 英文映射 + switch (n) { + case "伤害吸收": return "ABSORPTION"; + case "生命提升": return "HEALTH_BOOST"; + case "抗性提升": return "DAMAGE_RESISTANCE"; + case "抗火": return "FIRE_RESISTANCE"; + case "夜视": return "NIGHT_VISION"; + case "力量": return "INCREASE_DAMAGE"; + case "速度": return "SPEED"; + case "急迫": return "FAST_DIGGING"; + case "跳跃提升": return "JUMP"; + case "再生": return "REGENERATION"; + case "生命恢复": return "REGENERATION"; + case "治疗": return "HEAL"; + case "瞬间治疗": return "HEAL"; + case "伤害药水": return "HARM"; + case "瞬间伤害": return "HARM"; + case "虚弱": return "WEAKNESS"; + case "中毒": return "POISON"; + case "缓慢": return "SLOW"; + case "挖掘疲劳": return "SLOW_DIGGING"; + case "失明": return "BLINDNESS"; + case "隐身": return "INVISIBILITY"; + case "发光": return "GLOWING"; + // 村庄英雄 + case "村庄英雄": return "HERO_OF_THE_VILLAGE"; + case "幸运": return "LUCK"; + case "不幸": return "UNLUCK"; + case "水下呼吸": return "WATER_BREATHING"; + case "水下速掘": return "DOLPHINS_GRACE"; + case "缓降": return "LEVITATION"; + case "漂浮": return "LEVITATION"; + case "饱和": return "SATURATION"; + // 无效效果 + case "伤害": return "HARM"; + case "治疗药水": return "HEAL"; + default: + // 尝试直接用英文匹配 + return n; + } + } + + /** + * 全量容错:把全角字符统一转半角, + * 去掉零宽字符/不可见字符, + * 让用户怎么写都能正确解析。 + */ + private String normalizeLine(String input) { + if (input == null || input.isEmpty()) return ""; + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + + // 跳过零宽字符和不可见字符 + if (c == '\u200B' // 零宽空格 + || c == '\u200C' // 零宽非连接 + || c == '\u200D' // 零宽连接 + || c == '\uFEFF' // BOM + || c == '\u00A0' // 不间断空格 + || c == '\u2028' // 行分隔符 + || c == '\u2029' // 段分隔符 + || c == '\u200E' // LTR标记 + || c == '\u200F' // RTL标记 + || c == '\u2060' // 字连接符 + || (c >= '\u2000' && c <= '\u200A')) { // 各种空格 + continue; + } + + // 全角→半角映射 + if (c >= '\uFF01' && c <= '\uFF5E') { + c = (char) (c - 0xFEE0); + } + + // 全角空格→半角空格 + if (c == '\u3000') { + c = ' '; + } + + sb.append(c); + } + + return sb.toString(); + } + + + private AreaConfig parseArea(String name, File f) + throws IOException { + AreaConfig ac = new AreaConfig(); + ac.name = name; + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean inBlockComment = false; + boolean inHtmlComment = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty()) continue; +// ===== 全量容错预处理:全角→半角,去零宽字符 ===== + line = normalizeLine(line); + if (line.isEmpty()) continue; +// ===== 暴力调试:输出所有包含关键词的行 ===== + if (line.indexOf("游戏") >= 0 + || line.indexOf("模式") >= 0 + || line.indexOf("生存") >= 0 + || line.indexOf("创造") >= 0 + || line.indexOf("排除") >= 0 + || line.indexOf("豁免") >= 0 + || line.indexOf("强制") >= 0 + || line.indexOf("GameMode") >= 0 + || line.indexOf("SURVIVAL") >= 0 + || line.indexOf("CREATIVE") >= 0) { + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < line.length(); i++) { + hex.append(String.format("U+%04X ", (int) line.charAt(i))); + } + /* plugin.getLogger().info("[暴力调试] 行=[" + line + + "] 长度=" + line.length() + + " 字符码=" + hex.toString());*/ + } +// ===== 兜底扫描:确保和平白名单一定能解析 ===== + if (line.indexOf("白名单") >= 0 + || line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + int ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf(':'); + if (ci < 0) ci = line.indexOf('='); + if (ci < 0) ci = line.indexOf(' '); + + // 和平白名单 + if (line.indexOf("和平") >= 0 + && line.indexOf("白名单") >= 0 + && ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.peaceWhitelist.add(nm); + } + plugin.getLogger().info("[解析兜底] 和平白名单=" + + ac.peaceWhitelist); + } + + // 排除名单 + if (line.indexOf("排除名单") >= 0 + || line.indexOf("模式排除") >= 0 + || line.indexOf("模式豁免") >= 0) { + if (ci >= 0) { + String rest = line.substring(ci + 1).trim(); + rest = rest.replaceAll("[,,;;|、/]", ","); + for (String s : rest.split(",")) { + String nm = s.trim(); + if (!nm.isEmpty()) ac.modeExempt.add(nm); + } + plugin.getLogger().info("[解析兜底] 排除名单=" + + ac.modeExempt); + } + } + } +// ===== 兜底结束 ===== + + if (inBlockComment) { + int endIdx = line.indexOf("*/"); + if (endIdx >= 0) { + inBlockComment = false; + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("/*")) { + int endIdx = line.indexOf("*/", 2); + if (endIdx >= 0) { + line = line.substring(endIdx + 2).trim(); + if (line.isEmpty()) continue; + } else { + inBlockComment = true; + continue; + } + } + if (inHtmlComment) { + int endIdx = line.indexOf("-->"); + if (endIdx >= 0) { + inHtmlComment = false; + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + continue; + } + } + if (line.startsWith("", 4); + if (endIdx >= 0) { + line = line.substring(endIdx + 3).trim(); + if (line.isEmpty()) continue; + } else { + inHtmlComment = true; + continue; + } + } + if (line.startsWith("#") || line.startsWith("//")) { + continue; + } + int hashIdx = line.indexOf("#"); + if (hashIdx > 0) { + line = line.substring(0, hashIdx).trim(); + } + int slashIdx = line.indexOf("//"); + if (slashIdx > 0) { + line = line.substring(0, slashIdx).trim(); + } + if (line.isEmpty()) continue; + + if (line.startsWith("起点:")) { + String[] pp = line.substring("起点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x1 = Integer.parseInt(pp[1].trim()); + ac.z1 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("终点:")) { + String[] pp = line.substring("终点:".length()) + .trim().split(","); + if (pp.length == 3) { + ac.world = pp[0].trim(); + ac.x2 = Integer.parseInt(pp[1].trim()); + ac.z2 = Integer.parseInt(pp[2].trim()); + } + } else if (line.startsWith("高度范围:")) { + String[] pp = line.substring("高度范围:".length()) + .trim().split("-"); + if (pp.length == 2) { + ac.yMin = Integer.parseInt(pp[0].trim()); + ac.yMax = Integer.parseInt(pp[1].trim()); + } + } else if (line.startsWith("没收物品:")) { + for (String s : line.substring("没收物品:".length()) + .trim().split("[,,]")) + ac.confiscateItems.add( + s.trim().toUpperCase()); + } else if (line.startsWith("禁止使用物品:")) { + for (String s : line.substring("禁止使用物品:".length()) + .trim().split("[,,]")) + ac.denyUseItems.add( + s.trim().toUpperCase()); + } else if (line.equals("禁止放置方块")) { + ac.denyBlockPlace = true; + } else if (line.equals("禁止破坏方块")) { + ac.denyBlockBreak = true; + } else if (line.equals("禁止PVP")) { + ac.denyPVP = true; + } else if (line.equals("禁止摔伤")) { + ac.denyFallDamage = true; + } else if (line.equals("禁止饥饿")) { + ac.denyHunger = true; + } else if (line.equals("禁止一切伤害")) { + ac.denyAllDamage = true; + } else if (line.equals("禁止丢弃物品")) { + ac.denyDrop = true; + } else if (line.equals("禁止末影珍珠")) { + ac.denyEnderPearl = true; + } else if (line.equals("禁止使用弓箭")) { + ac.denyBow = true; + } else if (line.equals("禁止骑乘")) { + ac.denyMount = true; + } else if (line.equals("禁止袭击")) { + ac.denyRaid = true; + } else if (line.equals("禁止药水")) { + ac.denyPotion = true; + } else if (line.equals("禁止爆炸")) { + ac.denyExplosion = true; + } else if (line.equals("禁止燃烧") + || line.equals("禁止火焰蔓延")) { + ac.denyFireSpread = true; + } else if (line.equals("清除所有效果") + || line.equals("清理所有效果")) { + ac.clearAllBadEffects = true; + } else if (line.startsWith("清除") + || line.startsWith("清理")) { + String effName = line.substring(2).trim(); + if (effName.endsWith("效果")) + effName = effName.substring(0, + effName.length() - 2); + if (!effName.isEmpty()) + ac.clearEffects.add(effName); + } else if (line.contains("和平模式") + && !line.contains("白名单") + && !line.contains("定时") + && !line.contains("开关")) { + ac.peaceMode = true; + plugin.getLogger().info("[解析] 和平模式已识别"); + + } else if (line.startsWith("效果")) { + String rest = line.substring(2).trim(); + // 去掉前导冒号、空格 + while (rest.length() > 0 + && (rest.charAt(0) == ':' + || rest.charAt(0) == ':' + || rest.charAt(0) == ' ' + || rest.charAt(0) == '\t')) { + rest = rest.substring(1); + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + // 第一个是效果名(可能是中文) + String effName = parts[0]; + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) {} + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) {} + } + + // ★ 用映射方法转为英文名 + String resolved = resolveEffectName(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → " + resolved + + " 等级=" + effLv + + " 时长=" + effDur); + + ac.giveEffects.add(new String[]{ + resolved, + String.valueOf(effLv), + String.valueOf(effDur)}); + // ★ 加日志确认解析结果 + PotionEffectType checkType = + resolveEffectType(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → type=" + + (checkType != null + ? checkType.getName() + : "NULL(失败)") + + " 等级=" + effLv + + " 时长=" + effDur); + + + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) { + } + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) { + } + } + ac.giveEffects.add(new String[]{ + parts[0], + String.valueOf(effLv), + String.valueOf(effDur)}); + } + } else if (line.startsWith("通报批评:")) { + ac.announceTemplate = line.substring( + "通报批评:".length()).trim(); + ac.enableAnnounce = true; + } else if (line.startsWith("没收提示:")) { + ac.confiscateMsg = line.substring( + "没收提示:".length()).trim(); + } else if (line.startsWith("进入提示:")) { + String raw = line.substring("进入提示:".length()).trim(); + ac.enterMsg = raw; + + // ★ 调试:打印原文和Unicode + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★进入提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("离开提示:")) { + String raw = line.substring("离开提示:".length()).trim(); + ac.leaveMsg = raw; + + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★离开提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("惩罚命令:")) { + ac.punishCommands.add(line.substring( + "惩罚命令:".length()).trim()); + } else if (line.startsWith("强制游戏模式:") + || line.startsWith("游戏模式:")) { + String prefix = line.startsWith("强制游戏模式:") + ? "强制游戏模式:" : "游戏模式:"; + String modeStr = line.substring(prefix.length()) + .trim().toUpperCase(); + // 容错:支持多种写法 + if (modeStr.equals("CREATIVE") || modeStr.equals("创造") + || modeStr.equals("1")) { + ac.enforceGameMode = "CREATIVE"; + } else if (modeStr.equals("SURVIVAL") || modeStr.equals("生存") + || modeStr.equals("0")) { + ac.enforceGameMode = "SURVIVAL"; + } else if (modeStr.equals("ADVENTURE") || modeStr.equals("冒险") + || modeStr.equals("2")) { + ac.enforceGameMode = "ADVENTURE"; + } else if (modeStr.equals("SPECTATOR") || modeStr.equals("旁观") + || modeStr.equals("spectator") + || modeStr.equals("3")) { + ac.enforceGameMode = "SPECTATOR"; + } + } else if (line.contains("排除名单") + || line.contains("模式排除") + || line.contains("模式豁免")) { + int colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf(':'); + if (colonIdx < 0) colonIdx = line.indexOf('='); + if (colonIdx >= 0) { + String rest = line.substring(colonIdx + 1).trim(); + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playerName = s.trim(); + if (!playerName.isEmpty()) { + ac.modeExempt.add(playerName); + } + } + } + + String rest = line.substring(/*idx*/ + 4).trim(); + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String playername = s.trim(); + if (!name.isEmpty()) ac.modeExempt.add(name); + } + } + + while (rest.length() > 0) { + char c = rest.charAt(0); + if (c == ':' || c == ':' || c == '=' + || c == ' ' || c == '\t') { + rest = rest.substring(1); + } else { + break; + } + + } + if (!rest.isEmpty()) { + String cleaned = rest.replaceAll("[,,;;|、/]", ","); + for (String s : cleaned.split(",")) { + String wlName = s.trim(); + if (!wlName.isEmpty()) { + ac.peaceWhitelist.add(wlName); + } + } + } + + + + } else if (line.equals("禁止交互展示框") + || line.equals("禁止展示框")) { + ac.denyItemFrame = true; + + } + } + br.close(); + return ac; + } +// ===== 和平白名单操作 ===== + + // 添加生物名字到区域和平白名单 + public boolean addPeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.add(creatureName); + savePeaceWhitelist(ac); + return true; + } + + // 从区域和平白名单移除生物名字 + public boolean removePeaceName(String areaName, String creatureName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) return false; + ac.peaceWhitelist.remove(creatureName); + savePeaceWhitelist(ac); + return true; + } + +// ===== 模式排除名单操作 ===== + + + @EventHandler + public void onCreatureSpawn(CreatureSpawnEvent e) { + Entity entity = e.getEntity(); + if (!isHostile(entity.getType().name())) return; + + Location loc = entity.getLocation(); + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + + if (ac == null || !ac.peaceMode) return; + + protectedEntities.put( + entity.getUniqueId(), + System.currentTimeMillis() + 5000); + + /* plugin.getLogger().info("[和平] " + + entity.getType().name() + + " 获得5秒保护期" + + " 区域=" + ac.name + + " 白名单=" + ac.peaceWhitelist + + " 白名单数量=" + ac.peaceWhitelist.size());*/ + } + + + public void cleanupProtectedEntities() { + long now = System.currentTimeMillis(); + int cleaned = 0; + Iterator> it = + protectedEntities.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (now > entry.getValue()) { + it.remove(); + cleaned++; + } + } + if (cleaned > 0) { + /* plugin.getLogger().info("[和平模式] 清理过期保护: " + + cleaned + "个 剩余:" + + protectedEntities.size() + "个");*/ + } + } + + + public void banHostilesWithWhitelist(Player p, AreaConfig ac) { + long now = System.currentTimeMillis(); + // 看看附近到底有什么实体 + List nearby = p.getNearbyEntities(48, 48, 48); + /* plugin.getLogger().info("[和平] " + p.getName() + + " 附近实体总数=" + nearby.size());*/ + for (Entity ent : nearby) { + String typeName = ent.getType().name(); + /* plugin.getLogger().info("[和平] 实体: " + + typeName + " isHostile=" + isHostile(typeName));*/ + } + int scanned = 0; + int waiting = 0; + int saved = 0; + int banished = 0; + + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (!isHostile(ent.getType().name())) continue; + scanned++; + + UUID entUid = ent.getUniqueId(); + Long protectUntil = protectedEntities.get(entUid); + String customName = ent.getCustomName(); + + // 有保护期记录 + if (protectUntil != null) { + long remain = protectUntil - now; + + if (remain > 0) { + // 保护期内,精确剩余秒数 + int secondsLeft = (int) Math.ceil(remain / 1000.0); + waiting++; + + // 保护期内也检查名字(玩家可能刚贴上) + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + protectedEntities.remove(entUid); + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 白名单匹配,永久保留");*/ + continue; + } + + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 保护期剩余约" + secondsLeft + "秒");*/ + continue; + } + + // 保护期到了,最终检查 + protectedEntities.remove(entUid); + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + " 5秒到期");*/ + + // 到期时再查一次白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 到期但白名单匹配,保留");*/ + continue; + } + + // 没名字或不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名=[" + customName + "] 传送虚空");*/ + continue; + } + + // 没有保护期记录(非刷怪蛋生成的或记录被清了) + // 也检查一下白名单 + if (customName != null + && !customName.isEmpty() + && ac.peaceWhitelist.contains(customName)) { + saved++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 命名[" + customName + + "] 无保护期但白名单匹配,保留");*/ + continue; + } + + // 没有保护期且不在白名单,传送虚空 + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + banished++; + /* plugin.getLogger().info("[和平] " + + ent.getType().name() + + " 无保护期,命名=[" + customName + "] 传送虚空");*/ + } + + /* plugin.getLogger().info("[和平] " + + " 扫描=" + scanned + + " 等待=" + waiting + + " 保留=" + saved + + " 传送=" + banished + + " 白名单=" + ac.peaceWhitelist);*/ + } + + private static final Set HOSTILE_TYPES = new HashSet<>(Arrays.asList( + "ZOMBIE", "ZOMBIE_VILLAGER", "HUSK", "DROWNED", + "SKELETON", "STRAY", "BOGGED", "WITHER_SKELETON", + "CREEPER", "SPIDER", "CAVE_SPIDER", "ENDERMITE", + "SILVERFISH", "BLAZE", "GHAST", "BIG_GHAST", + "MAGMA_CUBE", "SLIME", "WITCH", + "GUARDIAN", "ELDER_GUARDIAN", + "PILLAGER", "VINDICATOR", "EVOKER", "RAVAGER", + "ILLUSIONER", "VEX", + "PHANTOM", "SHULKER", "WARDEN", "BREEZE", + "PIGLIN", "PIGLIN_BRUTE", "ZOGLIN", + "ZOMBIFIED_PIGLIN", "ENDER_DRAGON" + )); + + private boolean isHostile(String typeName) { + return HOSTILE_TYPES.contains(typeName); + } + + // ==================== 白名单持久化 ==================== + + /** + * 保存白名单到文件 + * 路径:区域防护/whitelists/全局白名单.txt + * 路径:区域防护/whitelists/区域名.txt + */ + public void saveWhitelists() { + try { + // 1. 保存全局白名单 + PrintWriter pwGlobal = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(globalWhitelistFile), + StandardCharsets.UTF_8)); + for (String name : globalPlayerWhitelist) { + pwGlobal.println(name); + } + pwGlobal.close(); + + // 2. 删除旧的区域白名单文件(防止残留) + File[] oldFiles = whitelistDir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (oldFiles != null) { + for (File f : oldFiles) f.delete(); + } + + // 3. 保存各区域白名单 + for (Map.Entry> entry + : areaPlayerWhitelist.entrySet()) { + String name = entry.getKey(); + File areaFile = new File( + whitelistDir, name + ".txt"); + PrintWriter pwArea = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(areaFile), + StandardCharsets.UTF_8)); + for (String playerName : entry.getValue()) { + pwArea.println(playerName); + } + pwArea.close(); + } + + plugin.getLogger().info("[防护] 白名单已保存"); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 保存白名单失败: " + + e.getMessage()); + } + } + + + + // ==================== 区域检测 ==================== + + public AreaConfig getArea(String worldName, + int x, int y, int z) { + for (AreaConfig ac : areas.values()) { + if (ac.world.equalsIgnoreCase(worldName) + && inRect(x, z, ac) + && y >= ac.yMin + && y <= ac.yMax) { + return ac; + } + } + return null; + } + + private boolean inRect(int x, int z, AreaConfig ac) { + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + return x >= minX && x <= maxX + && z >= minZ && z <= maxZ; + } + + // ==================== 白名单检查 ==================== + + private boolean isPlayerWhitelisted(String player, + AreaConfig ac) { + if (globalPlayerWhitelist.contains( + player.toLowerCase())) + return true; + Set areaList = + areaPlayerWhitelist.get(ac.name); + return areaList != null + && areaList.contains(player.toLowerCase()); + } + + private boolean isItemBlacklisted(String itemId, + AreaConfig ac) { + if (globalItemBlacklist.contains( + itemId.toUpperCase())) + return true; + Set areaList = + areaItemBlacklist.get(ac.name); + return areaList != null + && areaList.contains(itemId.toUpperCase()); + } + + // ==================== 没收 ==================== + + private int confiscateItem(Player p, String itemTypeId) { + Material mat = Material.matchMaterial(itemTypeId); + if (mat == null) return 0; + ItemStack[] contents = p.getInventory().getContents(); + int removed = 0; + for (int i = 0; i < contents.length; i++) { + if (contents[i] != null + && contents[i].getType() == mat) { + removed += contents[i].getAmount(); + contents[i] = null; + } + } + p.getInventory().setContents(contents); + return removed; + } + + // ==================== 边框显示 ==================== + private void showBorder(Player p, AreaConfig ac, boolean temp) { + cancelBorder(p); + int taskId = Bukkit.getScheduler() + .runTaskTimer(plugin, new Runnable() { + @Override + public void run() { + if (!p.isOnline()) return; + Location loc = p.getLocation(); + int px = loc.getBlockX(); + int py = loc.getBlockY(); + int pz = loc.getBlockZ(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + for (int dy = 5; dy <= 30; dy += 5) { + for (int x = minX; x <= maxX; x += 4) { + for (int z = minZ; z <= maxZ; z += 4) { + if (x == minX || x == maxX + || z == minZ || z == maxZ) { + p.spawnParticle(Particle.END_ROD, + x + 0.5, py + dy, z + 0.5, + 1, 0, 0, 0, 0); + } + } + } + } + } + }, 0L, 20L).getTaskId(); + displayTaskIds.put(p.getUniqueId(), taskId); // taskId 是 int + if (temp) { + Bukkit.getScheduler().runTaskLater(plugin, + () -> cancelBorder(p), 300L); + } + } + + + private void cancelBorder(Player p) { + Integer taskId = displayTaskIds.remove(p.getUniqueId()); + if (taskId != null) { + Bukkit.getScheduler().cancelTask(taskId); + } + } + + public void onPlayerQuit(String playerName) { + playerCurrentArea.remove(playerName); + } + + public void clearAllPlayerAreas() { + playerCurrentArea.clear(); + } + private void saveModeExempt(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("排除名单") + || trimmed.contains("模式排除") + || trimmed.contains("模式豁免")) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.modeExempt.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("排除名单:"); + boolean first = true; + for (String name : ac.modeExempt) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存模式排除名单失败: " + e.getMessage()); + } + } + + // ==================== 事件监听 ==================== + @EventHandler + public void onPlayerMove(PlayerMoveEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + Location to = event.getTo(); + if (to == null) return; + + Location from = event.getFrom(); + if (from.getBlockX() == to.getBlockX() + && from.getBlockZ() == to.getBlockZ()) { + return; + } + + // 防抖 + Long lastTime = lastMoveProcess.get(uid); + long now = System.currentTimeMillis(); + if (lastTime != null + && now - lastTime < MOVE_DEBOUNCE_MS) { + return; + } + lastMoveProcess.put(uid, now); + + // 检测区域 + String oldArea = playerCurrentArea.get(uid); + String newArea = null; + String forcedMode = null; + + for (Map.Entry entry + : areas.entrySet()) { + AreaConfig ac = entry.getValue(); + if (!ac.world.equals(to.getWorld().getName())) + continue; + int px = to.getBlockX(); + int pz = to.getBlockZ(); + int py = to.getBlockY(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + if (px >= minX && px <= maxX + && pz >= minZ && pz <= maxZ + && py >= ac.yMin && py <= ac.yMax) { + newArea = entry.getKey(); + forcedMode = ac.enforceGameMode; + break; + } + } + + /* plugin.getLogger().info( + "[防护-移动] " + p.getName() + + " 旧=[" + oldArea + + "] 新=[" + newArea + "]" + + " areas.size=" + areas.size() + + " world=" + to.getWorld().getName());*/ + + // ===== 2. 区域没变 ===== + if (Objects.equals(oldArea, newArea)) { + if (newArea != null) { + AreaConfig ac = areas.get(newArea); + if (ac != null) { + // ★ 每次移动都清除指定效果 + clearBadEffects(p, ac); + + // ★ 每次移动都强制游戏模式 + if (ac.enforceGameMode != null + && !ac.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + } + return; + } + + // 离开旧区域 + if (oldArea != null) { + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★开始离开处理: + + oldArea);*/ + + AreaConfig oldAc = areas.get(oldArea); + + // 离开提示 + if (oldAc != null && oldAc.leaveMsg != null + && !oldAc.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(oldAc.leaveMsg)); + } + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★收标: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + plugin.getLogger().info( + "[防护-移动-离开] 标记项: " + t.getName()); + } + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + plugin.getLogger().info( + "[防护-移动-离开] 取消旧延时"); + } + + // 创建延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = oldArea; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★创建延时任务: " + + toClear.size() + "个");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-移动-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); + + plugin.getLogger().info( + "[防护-移动-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size()); + + if (online == null || !online.isOnline()) { + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-移动-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-移动-清理] + cleared" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + } + } + + + // 进入新区域 + if (newArea != null) { + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + if (oldP != null) { + diffEffects(p, uid, oldP, newArea); + } + } + + AreaConfig newAc = areas.get(newArea); + if (newAc != null) { + if (newAc.enterMsg != null + && !newAc.enterMsg.isEmpty()) { + p.sendMessage( + formatAreaMsg(newAc.enterMsg)); + } + applyRegionEffects(p, uid, newArea, newAc); + // 清除效果 + clearBadEffects(p, newAc); + + } +// ===== 清除指定效果 ===== + if (newAc.clearEffects != null + && !newAc.clearEffects.isEmpty()) { + for (String effName : newAc.clearEffects) { + PotionEffectType type = + resolveEffectType(effName); + if (type != null && p.getPotionEffect(type) != null) { + p.removePotionEffect(type); + plugin.getLogger().info( + "[防护] ★清除效果: " + effName + + " 玩家=" + p.getName()); + } + } + } + +// ===== 清除所有效果 ===== + if (newAc.clearAllBadEffects) { + for (PotionEffect eff : p.getActivePotionEffects()) { + p.removePotionEffect(eff.getType()); + } + plugin.getLogger().info( + "[防护] ★清除所有效果, 玩家=" + p.getName()); + } + + if (forcedMode != null + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(forcedMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + if (newArea != null) { + playerCurrentArea.put(uid, newArea); + } else { + playerCurrentArea.remove(uid); + } + } +// ===== 清理袭击生物 ===== + + // 需要清理的袭击生物类型 + private static final Set RAID_MOBS = new HashSet<>(Arrays.asList( + "VEX", + "EVOKER", + "PILLAGER", + "VINDICATOR", + "WITCH", + "RAVAGER", + "PILLAGER" + )); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + Entity entity = event.getEntity(); + Location loc = entity.getLocation(); + + // 检查是否在防护区域内 + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + if (ac == null) return; + + // 禁止袭击:取消袭击事件 + if (ac.denyRaid) { + // 清理袭击生物 + String typeName = entity.getType().name(); + if (RAID_MOBS.contains(typeName)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] ★清理袭击生物: " + typeName + + " 区域=" + ac.name); + } + } + + // 没收物品检测 + if (ac.confiscateItems != null + && !ac.confiscateItems.isEmpty()) { + if (entity instanceof Item) { + Item item = (Item) entity; + String itemType = item.getItemStack() + .getType().name(); + if (ac.confiscateItems.contains(itemType)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] 没收违禁掉落物: " + + itemType); + } + } + } + } + + + /** + * 进入区域时发放效果 + * 流程:查DB → 有记录跳过写入 → 无记录写入DB + * → 给效果 → 贴标 + */ + private void applyRegionEffects(Player p, UUID uid, + String areaName, AreaConfig ac) { + /* plugin.getLogger().info( + "[防护-贴标] ★★★方法被调用! 玩家=" + + p.getName() + " 区域=" + areaName + + " ac=" + (ac != null) + + " 效果配置数=" + + (ac != null ? ac.giveEffects.size() : "null"));*/ + + if (ac == null || ac.giveEffects.isEmpty()) return; + if (ac.giveEffects.isEmpty()) return; + // 查DB:该玩家在此区域是否已有效果记录 + List dbEffects = + getPlayerEffectNames(uid, areaName); + + if (dbEffects.isEmpty()) { + // 无记录 → 写入DB + for (String[] eff : ac.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t == null) continue; + saveSingleEffect(uid, p.getName(), + areaName, t.getName()); + } + plugin.getLogger().info( + "[防护] DB写入, 玩家=" + p.getName() + + " 区域=" + areaName); + } else { + plugin.getLogger().info( + "[防护] DB已有记录, 跳过写入, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + + // 给效果 + 贴标 + List applied = new ArrayList<>(); + for (String[] eff : ac.giveEffects) { + try { + PotionEffectType type = + resolveEffectType(eff[0]); + if (type == null) continue; + int lv = Integer.parseInt(eff[1]) - 1; + int dur = Integer.parseInt(eff[2]) * 20; + + PotionEffect existing = + p.getPotionEffect(type); + if (existing != null + && existing.getAmplifier() >= lv) { + continue; + } + p.addPotionEffect( + new PotionEffect(type, dur, lv)); + applied.add(type); + } catch (Exception ignored) { + } + } + + // 贴标 + // 新代码(合并,不覆盖): + List existingMarks = + playerMarkedEffects.get(uid); + if (existingMarks != null && !existingMarks.isEmpty()) { + // 已有标记,合并新增的 + for (PotionEffectType t : applied) { + if (!existingMarks.contains(t)) { + existingMarks.add(t); + } + } + /* plugin.getLogger().info( + "[防护-贴标] 合并标记: " + + existingMarks.size() + + " 个, 玩家=" + p.getName());*/ + } else { + // 无标记,新建 + playerMarkedEffects.put(uid, applied); + /* plugin.getLogger().info( + "[防护-贴标] 新建标记: " + + applied.size() + + " 个, 玩家=" + p.getName());*/ + } + } + + /** + * 离开区域时,延时15秒清理效果 + * 流程:收标 → 15秒后清效果 → 确认清空 → 清DB + */ + private void scheduleEffectClear(Player p, UUID uid, + String areaName) { + // 收标 + List marked = + playerAppliedEffects.remove(uid); + + plugin.getLogger().info( + "[防护] 收标 " + (marked != null ? marked.size() : 0) + + " 个, 玩家=" + p.getName() + + " 区域=" + areaName); + + // 启动15秒延时 + pendingClearArea.put(uid, areaName); + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + + // 清除效果 + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + + // 确认清空后清DB + removePlayerEffects(uid, areaName); + plugin.getLogger().info( + "[防护] 15秒已到, 清除 " + + (marked != null ? marked.size() : 0) + + " 个效果 + DB, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + }.runTaskLater(plugin, 300L); // 15秒 = 300 tick + + pendingClearTask.put(uid, task); + } + public Set getAllAreaNames() { + return areas.keySet(); + } + + /** + * 15秒内进了新区域: + * 清除旧区域有、新区域没有的效果 + */ + private void diffEffects(Player p, UUID uid, + String oldAreaName, String newAreaName) { + AreaConfig oldAc = areas.get(oldAreaName); + AreaConfig newAc = areas.get(newAreaName); + if (oldAc == null || newAc == null) return; + + // ★ 从 pendingClearEffects 获取旧标记 + List oldMarked = + pendingClearEffects.remove(uid); + + if (oldMarked == null || oldMarked.isEmpty()) { + plugin.getLogger().info( + "[防护-跨区域] 无待清理效果"); + return; + } + + // 新区域的效果集合 + Set newTypes = new HashSet<>(); + if (newAc != null) { + for (String[] eff : newAc.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t != null) newTypes.add(t); + } + } + + int cleared = 0; + for (PotionEffectType type : oldMarked) { + if (!newTypes.contains(type)) { + p.removePotionEffect(type); + cleared++; + plugin.getLogger().info( + "[防护-跨区域] 清除: " + + type.getName()); + } else { + plugin.getLogger().info( + "[防护-跨区域] 保留: " + + type.getName() + + " (新区域也有)"); + } + } + + removePlayerEffects(uid, oldAreaName); + plugin.getLogger().info( + "[防护-跨区域] 完成: 清除" + + cleared + "/" + oldMarked.size() + + " 个, " + oldAreaName + + " → " + newAreaName); + } + +// ===== DB 辅助方法(简化版)===== + + /** + * 查询玩家在某个区域的效果记录(返回英文名列表) + */ + private List getPlayerEffectNames( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid=? AND area_name=?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("effect_type")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + /** + * 写入单条效果记录 + */ + private void saveSingleEffect(UUID uid, + String playerName, String areaName, + String effectType) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?,?,?,?,?,?,?)"); + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, effectType); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 写入效果失败: " + e.getMessage()); + } + } + + // ==================== 违禁品没收 ==================== + + /** + * 检查玩家是否豁免于没收 + * 全局白名单 / 区域白名单 / modeExempt / OP 都跳过 + */ + private boolean isExemptFromConfiscation( + Player p, String areaName) { + String name = p.getName(); + + if (globalPlayerWhitelist.contains(name)) + return true; + + Set areaWl = + areaPlayerWhitelist.get(areaName); + if (areaWl != null && areaWl.contains(name)) + return true; + + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) + return true; + + return false; + } + + /** + * 没收玩家背包中的违禁品 + */ + private void confiscateItems(Player p, + AreaConfig ac) { + boolean confiscated = false; + + for (int i = 0; + i < p.getInventory().getSize(); i++) { + ItemStack item = p.getInventory().getItem(i); + if (item == null) continue; + + String typeName = item.getType().name(); + if (ac.confiscateItems + .contains(typeName)) { + p.getInventory().setItem(i, null); + confiscated = true; + } + } + + if (confiscated) { + String msg = ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty() + ? ac.confiscateMsg + : "§c你的违禁品已被没收"; + p.sendMessage(msg); + + // 通报批评 + if (ac.enableAnnounce + && ac.announceTemplate != null) { + String text = ac.announceTemplate + .replace("{player}", + p.getName()); + Bukkit.broadcastMessage(text); + } + } + } + + + + private String getChineseGameMode(GameMode mode) { + switch (mode) { + case SURVIVAL: return "生存模式"; + case CREATIVE: return "创造模式"; + case ADVENTURE: return "冒险模式"; + case SPECTATOR: return "旁观模式"; + default: return mode.name(); + } + } + + private void handleEnter(Player p, AreaConfig ac) { + UUID uid = p.getUniqueId(); + + // 清除负面效果 + clearBadEffects(p, ac); + + // 给予效果 + List given = new ArrayList<>(); + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + given.add(type); + } + } + + removePlayerEffects(uid, ac.name); + if (!given.isEmpty()) { + // savePlayerEffects(uid, p.getName(), ac.name, given); + } + + // 白名单检查(最先执行) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 游戏模式切换(只对非白名单玩家) + if (ac.enforceGameMode != null) { + boolean exempt = isPlayerExemptFromModeChange(p, ac); + if (!exempt) { + GameMode target = GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + p.sendMessage("§e§l[区域防护] §f游戏模式已切换为: " + + getChineseGameMode(target)); + } + } + } + + // 和平模式/袭击检查 + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + handleConfiscate(p, ac); + } + + private boolean isPlayerExemptFromModeChange( + Player p, AreaConfig ac) { + // if (p.isOp()) return true; + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return true; + } + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return true; + } + if (ac.modeExempt.contains(p.getName())) { + return true; + } + return false; + } + + private void banRangedHostiles(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(48, 48, 48)) { + if (isRangedHostile(ent.getType().name())) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(ent.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + /** + * 按配置清除效果 + * denyRaid → 清除袭击相关 + * denyAllEffects → 清除所有负面+中性 + * denyAllEffects > denyRaid(全清优先) + */ + private void clearBadEffects(Player p, AreaConfig ac) { + // 和平模式 > 清除所有效果 > 禁止单个 + if (ac.peaceMode || ac.clearAllBadEffects) { + PotionEffectType[] allBad = { + PotionEffectType.SLOWNESS, + PotionEffectType.MINING_FATIGUE, + PotionEffectType.INSTANT_DAMAGE, + PotionEffectType.NAUSEA, + PotionEffectType.BLINDNESS, + PotionEffectType.HUNGER, + PotionEffectType.WEAKNESS, + PotionEffectType.POISON, + PotionEffectType.WITHER, + PotionEffectType.LEVITATION, + PotionEffectType.UNLUCK, + PotionEffectType.DARKNESS, + PotionEffectType.WIND_CHARGED, + PotionEffectType.WEAVING, + PotionEffectType.OOZING, + PotionEffectType.INFESTED, + PotionEffectType.BAD_OMEN, + PotionEffectType.TRIAL_OMEN, + PotionEffectType.RAID_OMEN + }; + for (PotionEffectType type : allBad) { + p.removePotionEffect(type); + } + return; + } + + // 禁止袭击 → 自动关联清除 + if (ac.denyRaid) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + p.removePotionEffect(PotionEffectType.RAID_OMEN); + } + + // 配置文件单个清除 + for (String effName : ac.clearEffects) { + PotionEffectType type = resolveEffectType(effName); + if (type != null) { + p.removePotionEffect(type); + } + } + } + + + private PotionEffectType resolveEffectType(String name) { + if (name == null || name.isEmpty()) return null; + + // 去掉"效果"后缀 + String clean = name; + if (clean.endsWith("效果")) { + clean = clean.substring(0, clean.length() - 2); + } + + // 硬编码中文名→英文ID映射(不依赖任何外部Map) + switch (clean) { + // 负面 + case "缓慢": + return resolveEffectType("slowness"); + case "挖掘疲劳": + return resolveEffectType("mining_fatigue"); + case "瞬间伤害": + return resolveEffectType("instant_damage"); + case "反胃": + return resolveEffectType("nausea"); + case "失明": + return resolveEffectType("blindness"); + case "饥饿": + return resolveEffectType("hunger"); + case "虚弱": + return resolveEffectType("weakness"); + case "中毒": + return resolveEffectType("poison"); + case "凋零": + return resolveEffectType("wither"); + case "飘浮": + return resolveEffectType("levitation"); + case "霉运": + return resolveEffectType("unluck"); + case "黑暗": + return resolveEffectType("darkness"); + case "蓄风": + return resolveEffectType("wind_charged"); + case "盘丝": + return resolveEffectType("weaving"); + case "渗浆": + return resolveEffectType("oozing"); + case "寄生": + return resolveEffectType("infested"); + // 中性 + case "不祥之兆": + case "不祥征兆": + return resolveEffectType("bad_omen"); + case "袭击之兆": + case "袭击征兆": + return resolveEffectType("raid_omen"); + case "试炼之兆": + case "试炼征兆": + return resolveEffectType("trial_omen"); + // 正面 + case "迅捷": + return resolveEffectType("speed"); + case "急迫": + return resolveEffectType("haste"); + case "力量": + return resolveEffectType("strength"); + case "瞬间治疗": + return resolveEffectType("instant_health"); + case "跳跃提升": + return resolveEffectType("jump_boost"); + case "生命恢复": + return resolveEffectType("regeneration"); + case "抗性提升": + return resolveEffectType("resistance"); + case "抗火": + return resolveEffectType("fire_resistance"); + case "水下呼吸": + return resolveEffectType("water_breathing"); + case "隐身": + return resolveEffectType("invisibility"); + case "夜视": + return resolveEffectType("night_vision"); + case "发光": + return resolveEffectType("glowing"); + case "生命提升": + return resolveEffectType("health_boost"); + case "伤害吸收": + return resolveEffectType("absorption"); + case "饱和": + return resolveEffectType("saturation"); + case "幸运": + return resolveEffectType("luck"); + case "村庄英雄": + return resolveEffectType("hero_of_the_village"); + case "缓降": + return resolveEffectType("slow_falling"); + case "潮涌能量": + return resolveEffectType("conduit_power"); + case "海豚的恩惠": + return resolveEffectType("dolphins_grace"); + case "致命中毒": + return resolveEffectType("poison"); + default: + break; + } + + +// 兜底:直接用原版英文ID查(★ 注意是 PotionEffectType.getByName,不是 resolveEffectType) + PotionEffectType fallback = PotionEffectType.getByName(clean); + if (fallback != null) return fallback; + fallback = PotionEffectType.getByName(clean.toLowerCase()); + if (fallback != null) return fallback; + + plugin.getLogger().warning("[防护] 无法识别效果: " + name); + return null; + + } + + + private static final Map EFFECT_MAP + = new HashMap<>(); + + + private void clearRaidEffects(Player p) { + boolean cleared = false; + try { + if (p.hasPotionEffect(PotionEffectType.BAD_OMEN)) { + p.removePotionEffect(PotionEffectType.BAD_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + try { + if (p.hasPotionEffect(PotionEffectType.RAID_OMEN)) { + p.removePotionEffect(PotionEffectType.RAID_OMEN); + cleared = true; + } + } catch (Exception ignored) { + } + if (cleared) { + p.sendMessage("§c§l[区域防护] §f已清除袭击相关效果"); + } + } + + public Map> + getPendingClearEffects() { + return pendingClearEffects; + } + + public void onPlayerOffline(UUID uid, String playerName, + String areaName) { + // 清除该玩家在该区域的所有效果 + List given = getPlayerEffects(uid, areaName); + for (PotionEffectType type : given) { + // 只清除我们给予的效果 + Player p = Bukkit.getPlayer(uid); + if (p != null) { + p.removePotionEffect(type); + } + } + // 清除DB记录 + removePlayerEffects(uid, areaName); + plugin.getLogger().info("[防护] " + playerName + + " 下线,已清除" + given.size() + + "个效果并清理DB"); + } + + @EventHandler + public void onTeleport(PlayerTeleportEvent e) { + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + + if (from.getWorld() == to.getWorld() + && from.distance(to) < 5) return; + + Player p = e.getPlayer(); + UUID uid = p.getUniqueId(); + + // 标记传送中 + teleporting.add(uid); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + teleporting.remove(uid); + }, 5L); + + AreaConfig fromArea = getArea( + from.getWorld().getName(), + from.getBlockX(), from.getBlockY(), + from.getBlockZ()); + AreaConfig toArea = getArea( + to.getWorld().getName(), + to.getBlockX(), to.getBlockY(), + to.getBlockZ()); + + String fromName = fromArea != null + ? fromArea.name : null; + String toName = toArea != null + ? toArea.name : null; + + /* plugin.getLogger().info( + "[防护-传送] ★★★进入onTeleport: " + + p.getName() + + " [" + fromName + "] → [" + toName + "]" + + " teleporting=" + teleporting.contains(uid));*/ + + if (Objects.equals(fromName, toName)) { + /* plugin.getLogger().info( + "[防护-传送] 区域相同, 跳过");*/ + return; + } + + // ===== 离开旧区域 ===== + if (fromName != null) { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★开始离开处理: " + + fromName);*/ + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★收标结果: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + /* plugin.getLogger().info( + "[防护-传送-离开] 标记项: " + + t.getName());*/ + } + } else { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★标记为null! " + + "尝试其他key查找...");*/ + // 遍历查找可能的标记 + for (Map.Entry> entry + : playerMarkedEffects.entrySet()) { + if (entry.getKey().equals(uid)) { + /* plugin.getLogger().info( + "[防护-传送-离开] 找到标记! 效果数=" + + entry.getValue().size());*/ + } + } + /* plugin.getLogger().info( + "[防护-传送-离开] playerMarkedEffects大小=" + + playerMarkedEffects.size());*/ + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 创建15秒延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = fromName; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★创建延时任务: " + + toClear.size() + "个效果");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-传送-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); +/* + plugin.getLogger().info( + "[防护-传送-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size() + + " 区域=" + clearArea);*/ + + if (online == null + || !online.isOnline()) { + plugin.getLogger().info( + "[防护-传送-清理] 玩家不在线, 清DB"); + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-传送-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-传送-清理] ★★★完成: 清除" + + cleared + "/" + toClear.size()); + } + }.runTaskLater(plugin, 300L); + pendingClearTask.put(uid, task); + + } + + // 离开提示 + if (fromArea != null && fromArea.leaveMsg != null + && !fromArea.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(fromArea.leaveMsg)); + } + } + + // ===== 进入新区域 ===== + if (toName != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] ★★★开始进入处理: " + + toName);*/ + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + plugin.getLogger().info( + "[防护-传送-进入] 取消旧延时, 旧区域=" + + oldP); + if (oldP != null) { + diffEffects(p, uid, oldP, toName); + } + } + + // 进入提示 + if (toArea != null && toArea.enterMsg != null + && !toArea.enterMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(toArea.enterMsg)); + } + + // 发放效果 + if (toArea != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] 调用applyRegionEffects");*/ + applyRegionEffects(p, uid, toName, toArea); +// 清除效果 + clearBadEffects(p, toArea); + + if (toArea.confiscateItems != null + && !toArea.confiscateItems.isEmpty() + && !isExemptFromConfiscation( + p, toName)) { + confiscateItems(p, toArea); + } + } + + // 强制模式 + if (toArea != null && toArea.enforceGameMode != null + && !toArea.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, toName)) { + GameMode target = GameMode.valueOf( + toArea.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + // 更新记录 + if (toName != null) { + playerCurrentArea.put(uid, toName); + } else { + playerCurrentArea.remove(uid); + } + } + + + public Map getPendingClearTasks() { + return pendingClearTask; + } + + public Map> + getPlayerMarkedEffects() { + return playerMarkedEffects; + } + + public void handleInside(Player p, AreaConfig ac) { + clearBadEffects(p, ac); + + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + // 全白或区白 → 跳过所有限制性规则(包括没收) + if (isPlayerWhitelisted(p.getName(), ac)) return; + + handleConfiscate(p, ac); + } + + public void cleanupExpiredProtections() { + protectedEntities.entrySet().removeIf( + entry -> System.currentTimeMillis() > entry.getValue()); + } + + // 禁止袭击:只驱逐袭击怪物 + private void banRaidMobs(Player p, AreaConfig ac) { + for (Entity ent : p.getNearbyEntities(32, 32, 32)) { + String t = ent.getType().name(); + if (t.equals("PILLAGER") + || t.equals("VINDICATOR") + || t.equals("EVOKER") + || t.equals("RAVAGER") + || t.equals("VEX") + || t.equals("WITCH") + || t.equals("ILLUSIONER") + || t.equals("ZOMBIE_VILLAGER")) { + Location voidLoc = ent.getLocation().clone(); + voidLoc.setY(voidLoc.getWorld().getMinHeight() - 50); + ent.teleport(voidLoc); + } + } + } + + public Set getAreaNames() { + return areas.keySet(); + } + + public Collection getPendingTasks() { + return pendingClearTask.values(); + } + + + // 判断是否为远程攻击敌对生物 + private boolean isRangedHostile(String typeName) { + switch (typeName) { + // ===== 骷髅系 ===== + case "SKELETON": // 骷髅(弓箭) + case "STRAY": // 流浪者(迟缓之箭) + case "BOGGED": // 虫蚀骷髅(毒箭) + case "WITHER_SKELETON": // 凋灵骷髅(近战但算灾厄) + + // ===== 灾厄村民 ===== + case "PILLAGER": // 掠夺者(弩) + case "ILLUSIONER": // 幻术师(弓箭+失明) + case "EVOKER": // 幻魔者(唤魔) + case "VINDICATOR": // 卫道士(斧头近战) + case "VEX": // 恼鬼(剑近战,但会穿墙飞行) + + // ===== 法术/投射 ===== + case "BLAZE": // 烈焰人(火球) + case "WITCH": // 女巫(药水) + + // ===== 飞行/爆炸 ===== + case "GHAST": // 恶魂(火球) + case "PHANTOM": // 幻翼(俯冲) + case "BIG_GHAST": // 大恶魂(1.21新增) + + // ===== 水下 ===== + case "GUARDIAN": // 守卫者(激光) + case "ELDER_GUARDIAN": // 远古守卫者(激光) + + // ===== 远程特殊 ===== + case "SHULKER": // 潜影贝(潜影弹) + case "BREEZE": // 旋风人(风弹) + + // ===== 蜘蛛系(远程毒液攻击) ===== + case "SPIDER": // 蜘蛛(白天远程吐丝) + case "CAVE_SPIDER": // 洞穴蜘蛛(毒) + + // ===== 1.21新增 ===== + case "WIND_CHARGED": // 蓄风怪(风弹) + case "WEAVING": // 盘丝怪(吐丝) + case "OOZING": // 渗浆怪(史莱姆) + case "INFESTED": // 寄生怪(蠹虫) + + // ===== 盔甲/变种(部分版本) ===== + case "ARMORED_SKELETON": // 盔甲骷髅 + case "ARCHER_SKELETON": // 弓箭骷髅(部分版本) + case "SNOW_GOLEM": // 雪傀儡(不是敌对但远程) + + // ===== 亡灵骑乘 ===== + case "HUSK": // 尸壳(近战但属于灾厄系) + case "DROWNED": // 溺尸(三叉戟远程) + case "ZOMBIE_VILLAGER": // 僵尸村民 + case "ZOMBIE_PIGMAN": // 僵尸猪灵 + case "ZOMBIFIED_PIGLIN": // 僵尸猪灵(1.16+) + case "PIGLIN_BRUTE": // 猪灵蛮兵 + + // ===== 猪灵(弓箭) ===== + case "PIGLIN": // 猪灵(金弩远程) + + // ===== 末影系 ===== + case "ENDER_DRAGON": // 末影龙(远程吐息) + case "WARDEN": // 监守者(远程音波) + + // ===== 其他 ===== + case "CREPER": // 苦力怕(爆炸不算远程但危险) + case "CREEPER": // 苦力怕 + + return true; + default: + return false; + } + } + + + + private void applyEffects(Player p, AreaConfig ac) { + for (String[] parts : ac.giveEffects) { + String effName = parts[0]; + int level = 1; + int duration = 999; + try { level = Integer.parseInt(parts[1]); } + catch (NumberFormatException ignored) {} + try { duration = Integer.parseInt(parts[2]); } + catch (NumberFormatException ignored) {} + + PotionEffectType type = resolveEffectType(effName); + plugin.getLogger().info("[防护调试] 给予: " + + effName + " -> " + type); + if (type != null) { + p.addPotionEffect(new PotionEffect( + type, duration * 20, level - 1)); + plugin.getLogger().info("[防护调试] 已给予: " + + effName + " 等级" + level + + " 持续" + duration + "秒"); + } + } + } + + + @EventHandler + public void onBlockPlace(BlockPlaceEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockPlace + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止放置方块"); + } + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + e.getBlock().getX(), + e.getBlock().getY(), + e.getBlock().getZ()); + if (ac != null && ac.denyBlockBreak + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏方块"); + } + } + + + @EventHandler + public void onDamage(EntityDamageEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + // 保护性规则:对所有人无条件生效 + if (ac.denyAllDamage) { e.setCancelled(true); return; } + if (ac.denyFallDamage + && e.getCause() == EntityDamageEvent.DamageCause.FALL) + e.setCancelled(true); + } + + + @EventHandler + public void onPVP(EntityDamageByEntityEvent e) { + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyPVP + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + + @EventHandler + public void onFood(FoodLevelChangeEvent e) { + if (!(e.getEntity() instanceof Player)) return; + Player p = (Player) e.getEntity(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyHunger) + e.setCancelled(true); + } + + + + @EventHandler + public void onDrop(PlayerDropItemEvent e) { + Player p = e.getPlayer(); + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null && ac.denyDrop + && !isPlayerWhitelisted(p.getName(), ac)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止丢弃物品"); + } + } + + // 替换整个 onInteract 方法 + @EventHandler + public void onInteract(PlayerInteractEvent e) { + Player p = e.getPlayer(); + ItemStack hand = p.getInventory().getItemInMainHand(); + if (hand == null) return; + + // ===== 选区工具 ===== + if (hand.getType() == Material.BREEZE_ROD) { + ItemMeta meta = hand.getItemMeta(); + if (meta != null && meta.getDisplayName() != null + && meta.getDisplayName().contains("区域选择")) { + Action action = e.getAction(); + boolean isLeft = (action == Action.LEFT_CLICK_AIR + || action == Action.LEFT_CLICK_BLOCK); + boolean isRight = (action == Action.RIGHT_CLICK_AIR + || action == Action.RIGHT_CLICK_BLOCK); + if (isLeft || isRight) { + e.setCancelled(true); + Block block = e.getClickedBlock(); + if (block == null) { + try { block = p.getTargetBlockExact(5); } + catch (Exception ignored) {} + } + if (block == null) return; + UUID uid = p.getUniqueId(); + if (isLeft) { + pos1.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置1: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } else { + pos2.put(uid, block.getLocation()); + p.sendMessage("§a§l[防护] §f位置2: " + + block.getX() + ", " + + block.getY() + ", " + + block.getZ()); + } + } + return; + } + } + + // ===== 放置实体类物品时不检查(展示框、矿车、船、地图等)===== + Material mat = hand.getType(); + if (mat == Material.ITEM_FRAME + || mat == Material.GLOW_ITEM_FRAME + || mat == Material.MINECART + || mat == Material.TNT_MINECART + || mat == Material.CHEST_MINECART + || mat == Material.HOPPER_MINECART + || mat == Material.OAK_BOAT + || mat == Material.SPRUCE_BOAT + || mat == Material.BIRCH_BOAT + || mat == Material.JUNGLE_BOAT + || mat == Material.ACACIA_BOAT + || mat == Material.DARK_OAK_BOAT + || mat == Material.MANGROVE_BOAT + || mat == Material.CHERRY_BOAT + || mat == Material.BAMBOO_RAFT + || mat == Material.MAP + || mat == Material.ARMOR_STAND) { + return; + } + + // ===== 区域规则检查 ===== + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) return; + + // 末影珍珠禁止 + if (ac.denyEnderPearl + && hand.getType() == Material.ENDER_PEARL) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用末影珍珠"); + return; + } + + // 弓箭禁止 + if (ac.denyBow + && (hand.getType() == Material.BOW + || hand.getType() == Material.CROSSBOW)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用弓箭"); + return; + } + + // 白名单跳过 + if (isPlayerWhitelisted(p.getName(), ac)) return; + + // 禁止使用物品 + String typeId = hand.getType().name(); + if (ac.denyUseItems.contains(typeId)) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止使用此物品"); + return; + } + + // 没收检查 + handleConfiscate(p, ac); + } + + + + @EventHandler + public void onExplosion(EntityExplodeEvent e) { + Iterator it = e.blockList().iterator(); + while (it.hasNext()) { + Block b = it.next(); + AreaConfig ac = getArea( + b.getWorld().getName(), + b.getX(), b.getY(), b.getZ()); + if (ac != null && ac.denyExplosion) + it.remove(); + } + } + public void loadWhitelists() { + globalPlayerWhitelist.clear(); + areaPlayerWhitelist.clear(); + + if (!whitelistDir.exists()) { + whitelistDir.mkdirs(); + return; + } + + try { + // 加载全局白名单 + if (globalWhitelistFile.exists()) { + List lines = + java.nio.file.Files.readAllLines( + globalWhitelistFile.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + globalPlayerWhitelist.add(name); + } + } + } + + // 加载各区域白名单 + File[] files = whitelistDir.listFiles( + (File d, String n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (files != null) { + for (File f : files) { + String areaName = f.getName() + .replace(".txt", ""); + Set set = + ConcurrentHashMap.newKeySet(); + List lines = + java.nio.file.Files.readAllLines( + f.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + set.add(name); + } + } + if (!set.isEmpty()) { + areaPlayerWhitelist.put( + areaName, set); + } + } + } + + plugin.getLogger().info( + "[防护] 白名单加载完成,全局: " + + globalPlayerWhitelist.size() + + ",区域数: " + + areaPlayerWhitelist.size()); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载白名单失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onPotionSplash(PotionSplashEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + AreaConfig ac = getArea( + shooter.getWorld().getName(), + shooter.getLocation().getBlockX(), + shooter.getLocation().getBlockY(), + shooter.getLocation().getBlockZ()); + if (ac != null && ac.denyPotion + && !isPlayerWhitelisted(shooter.getName(), ac)) { + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止使用药水"); + } + } + + /** + * 解析区域名:先精准匹配,再模糊匹配 + * @param input 用户输入的区域名 + * @return 匹配到的区域名,无匹配返回null + */ + public String resolveAreaName(String input) { + if (input == null || input.isEmpty()) return null; + if (input.equalsIgnoreCase("global")) return "global"; + + // 第一步:精准匹配 + for (String name : areas.keySet()) { + if (name.equalsIgnoreCase(input)) { + return name; + } + } + + // 第二步:包含匹配(用户只打了部分名字) + for (String name : areas.keySet()) { + if (name.contains(input) || input.contains(name)) { + return name; + } + } + + // 第三步:编辑距离模糊匹配(著称→主城) + String bestMatch = null; + int bestDistance = Integer.MAX_VALUE; + for (String name : areas.keySet()) { + int dist = levenshteinDistance( + input.toLowerCase(), name.toLowerCase()); + if (dist < bestDistance) { + bestDistance = dist; + bestMatch = name; + } + } + + // 容错阈值:编辑距离 <= 名字长度的一半 + if (bestMatch != null) { + int threshold = Math.max(1, bestMatch.length() / 2); + if (bestDistance <= threshold) { + return bestMatch; + } + } + + return null; + } + + /** + * 判断字符串是否是区域名(精准或模糊) + */ + private boolean isAreaName(String name) { + if (name.equalsIgnoreCase("global")) return false; + return resolveAreaName(name) != null; + } + + /** + * 获取模糊匹配的实际区域名(用于提示用户) + */ + private String getResolvedName(String input) { + return resolveAreaName(input); + } + + /** + * 编辑距离算法(莱文斯坦距离) + */ + private int levenshteinDistance(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + for (int i = 0; i <= s1.length(); i++) dp[i][0] = i; + for (int j = 0; j <= s2.length(); j++) dp[0][j] = j; + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], + Math.min(dp[i - 1][j], dp[i][j - 1])); + } + } + } + return dp[s1.length()][s2.length()]; + } + + /** + * 智能重排参数:找出子命令位置,重排为标准顺序 + * 标准顺序:/protect 子命令 参数1 参数2 ... + */ + private String[] smartReorderArgs(String[] original) { + if (original.length <= 1) return original; + + Set knownSubs = new HashSet<>(Arrays.asList( + "add", "remove", "additem", "removeitem", + "addname", "removename", "listname", + "addwhite", "removewhite", "listwhite", + "list", "listitem", "创建", "删除", "重载", + "工具", "expand", "contraction", + "on", "off", "tempon", "modeexempt" + )); + + String first = original[0].toLowerCase(); + if (knownSubs.contains(first)) { + return original; + } + + int subIndex = -1; + for (int i = 0; i < original.length; i++) { + if (knownSubs.contains(original[i].toLowerCase())) { + subIndex = i; + break; + } + } + + if (subIndex < 0) { + return original; + } + + String[] reordered = new String[original.length]; + reordered[0] = original[subIndex]; + int idx = 1; + for (int i = 0; i < original.length; i++) { + if (i == subIndex) continue; + reordered[idx] = original[i]; + idx++; + } + + return reordered; + } + + // ==================== 管理命令 ==================== + public boolean handleCommand(CommandSender sender, + String[] args) { + args = smartReorderArgs(args); + if (args.length == 0) { + showHelp(sender); + return true; + } + String sub = args[0].toLowerCase(); +// ===== 诊断命令 ===== + if (sub.equals("sdf1debug")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + sender.sendMessage("§e===== 调试信息 ====="); + sender.sendMessage("§7当前区域: " + + playerCurrentArea.get(uid)); + sender.sendMessage("§7标记效果数: " + + (playerMarkedEffects.containsKey(uid) + ? playerMarkedEffects.get(uid).size() + : "无标记")); + sender.sendMessage("§7待清理任务: " + + pendingClearTask.containsKey(uid)); + sender.sendMessage("§7区域数: " + areas.size()); + + if (playerMarkedEffects.containsKey(uid)) { + List marked = + playerMarkedEffects.get(uid); + for (PotionEffectType type : marked) { + sender.sendMessage("§7 标记: " + + type.getName() + + " 身上有=" + + (p.getPotionEffect(type) != null)); + } + } + return true; + } + +// ===== 强制清理命令(测试用)===== + if (sub.equals("testclear")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + List marked = + playerMarkedEffects.remove(uid); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + sender.sendMessage("§a已强制清理 " + + marked.size() + " 个效果"); + } else { + sender.sendMessage("§c无标记效果可清理"); + } + + // 取消延时任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + sender.sendMessage("§a已取消延时任务"); + } + + removePlayerEffects(uid, + playerCurrentArea.getOrDefault(uid, "")); + sender.sendMessage("§aDB已清理"); + return true; + } + + // ===== 工具 ===== + if (sub.equals("工具") || sub.equals("wand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + ItemStack tool = new ItemStack(WAND); + ItemMeta meta = tool.getItemMeta(); + meta.setDisplayName("§a§l区域选择工具"); + meta.setLore(Arrays.asList( + "§7左键: 位置1", "§7右键: 位置2")); + tool.setItemMeta(meta); + p.getInventory().addItem(tool); + p.sendMessage("§a§l[防护] §f已获取选择工具"); + return true; + } + + // ===== 创建 ===== + if (sub.equals("创建") || sub.equals("create")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 创建 <名>"); + return true; + } + Player p = (Player) sender; + UUID u = p.getUniqueId(); + if (!pos1.containsKey(u) + || !pos2.containsKey(u)) { + p.sendMessage("§c请先用工具选点"); + return true; + } + String areaName = args[1]; + File f = new File(rootDir, areaName + ".txt"); + if (f.exists()) { + p.sendMessage("§c区域已存在"); + return true; + } + Location l1 = pos1.get(u); + Location l2 = pos2.get(u); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 区域: " + areaName); + pw.println("起点: " + l1.getWorld().getName() + + "," + l1.getBlockX() + + "," + l1.getBlockZ()); + pw.println("终点: " + l2.getWorld().getName() + + "," + l2.getBlockX() + + "," + l2.getBlockZ()); + pw.println("高度范围: 0-255"); + pw.println(); + pw.println("# 禁止放置方块"); + pw.println("# 禁止破坏方块"); + pw.println("# 禁止PVP"); + pw.println("# 效果: 夜视 1 999"); + pw.println("# 进入提示: 欢迎来到保护区"); + pw.println("# 离开提示: 已离开保护区"); + pw.close(); + p.sendMessage("§a§l[防护] §f区域 " + + areaName + " 已创建"); + loadAllAreas(); + } catch (IOException ex) { + p.sendMessage("§c创建失败"); + } + return true; + } + + // ===== 和平白名单 ===== + if (sub.equals("addname")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别: " + + args[1] + " 或 " + args[2]); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 和平白名单"); + return true; + } + sender.sendMessage("§c用法: /protect addname " + + "<区域> <名字>"); + return true; + } + if (sub.equals("removename")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.peaceWhitelist.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + parsed[0]); + return true; + } + sender.sendMessage("§c用法: /protect removename " + + "<区域> <名字>"); + return true; + } + if (sub.equals("listname")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listname <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 和平白名单:"); + if (ac.peaceWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.peaceWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 模式排除 ===== + if (sub.equals("addwhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect addwhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("removewhite")) { + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + AreaConfig ac = areas.get(parsed[0]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + ac.modeExempt.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + + parsed[0] + " 模式排除"); + return true; + } + sender.sendMessage("§c用法: /protect removewhite " + + "<区域> <玩家>"); + return true; + } + if (sub.equals("listwhite")) { + if (args.length < 2) { + sender.sendMessage( + "§c用法: /protect listwhite <区域>"); + return true; + } + AreaConfig ac = areas.get(args[1]); + if (ac == null) { + sender.sendMessage("§c区域不存在"); + return true; + } + sender.sendMessage("§a" + args[1] + + " 模式排除名单:"); + if (ac.modeExempt.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : ac.modeExempt) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + + // ===== 列表 ===== + if (sub.equals("列表") || sub.equals("list")) { + if (args.length == 2) { + if (args[1].equalsIgnoreCase("global")) { + sender.sendMessage("§a全局白名单:"); + if (globalPlayerWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n + : globalPlayerWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + String an = args[1]; + Set wl = + areaPlayerWhitelist.get(an); + sender.sendMessage("§a" + an + " 白名单:"); + if (wl == null || wl.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : wl) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + if (areas.isEmpty()) { + sender.sendMessage("§7暂无区域"); + } else { + sender.sendMessage("§e§l区域列表:"); + for (Map.Entry en + : areas.entrySet()) { + sender.sendMessage("§a " + en.getKey() + + " §7规则:" + + en.getValue().ruleCount() + + "条"); + } + } + return true; + } + + // ===== 重载 ===== + if (sub.equals("重载") || sub.equals("reload")) { + reload(); + sender.sendMessage("§a已重载 " + + areas.size() + " 个区域"); + return true; + } + + // ===== 物品黑名单 ===== + if (sub.equals("additem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + args[1]); + return true; + } + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + itemName); + return true; + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + } else { + areaItemBlacklist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()) + .add(itemName); + sender.sendMessage("§a已加黑: " + + itemName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect additem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("removeitem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (!globalItemBlacklist + .contains(itemName)) { + sender.sendMessage( + "§c全局黑名单中没有: " + + itemName); + return true; + } + globalItemBlacklist.remove(itemName); + sender.sendMessage("§a已移除: " + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.remove(itemName); + } else { + Set list = + areaItemBlacklist.get(areaName); + if (list != null) { + list.remove(itemName); + } + } + sender.sendMessage("§a已移除: " + + itemName); + return true; + } + sender.sendMessage("§c用法: /protect removeitem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("listitem")) { + if (args.length < 2) { + sender.sendMessage( + "§a全局物品黑名单:"); + if (globalItemBlacklist.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item + : globalItemBlacklist) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + Set list = + areaItemBlacklist.get(args[1]); + sender.sendMessage("§a" + args[1] + + " 物品黑名单:"); + if (list == null || list.isEmpty()) { + sender.sendMessage("§7(空)"); + } else { + for (String item : list) { + sender.sendMessage("§f- " + item); + } + } + return true; + } + + // ===== expand 扩建 ===== + if (sub.equals("expand")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ -= amount; + else if (dir.equals("南")) maxZ += amount; + else if (dir.equals("东")) maxX += amount; + else if (dir.equals("西")) minX -= amount; + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f扩建 §e" + + amount + "§f格"); + return true; + } + + // ===== contract 收缩 ===== + if (sub.equals("contraction") + || sub.equals("contract")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + int amount = 5; + if (args.length >= 2) { + try { + amount = Integer.parseInt(args[1]); + } catch (NumberFormatException ignored) {} + } + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + float yaw = p.getLocation().getYaw(); + String dir = yawToDir(yaw); + if (dir.equals("北")) minZ += amount; + else if (dir.equals("南")) maxZ -= amount; + else if (dir.equals("东")) maxX -= amount; + else if (dir.equals("西")) minX += amount; + if (minX > maxX || minZ > maxZ) { + p.sendMessage("§c收缩过度"); + return true; + } + ac.x1 = minX; + ac.x2 = maxX; + ac.z1 = minZ; + ac.z2 = maxZ; + saveAreaConfig(ac); + p.sendMessage("§a§l[防护] §f区域 §e" + ac.name + + " §f向 §e" + dir + " §f收缩 §e" + + amount + "§f格"); + return true; + } + + // ===== 删除 ===== + if (sub.equals("删除") || sub.equals("delete")) { + if (args.length < 2) { + sender.sendMessage( + "§e用法: /protect 删除 <名>"); + return true; + } + File f = new File(rootDir, args[1] + ".txt"); + if (f.exists()) { + f.delete(); + areas.remove(args[1]); + sender.sendMessage("§a已删除: " + args[1]); + } else { + sender.sendMessage("§c不存在: " + args[1]); + } + return true; + } + + // ===== add 加白名单 ===== + if (sub.equals("add")) { + if (args.length == 2) { + String name = args[1]; + if (globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 已在全局白名单中"); + return true; + } + globalPlayerWhitelist.add(name); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + + playerName); + } else { + Set wl = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()); + wl.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已加白: " + + playerName + " → " + areaName); + } + return true; + } + sender.sendMessage("§c用法: /protect add " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== remove 移除白名单 ===== + if (sub.equals("remove")) { + if (args.length == 2) { + String name = args[1]; + if (!globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 不在全局白名单中"); + return true; + } + globalPlayerWhitelist.remove(name); + saveWhitelists(); + sender.sendMessage("§a已从全局白名单移除: " + + name); + return true; + } + if (args.length == 3) { + String[] parsed = parseAreaAndTarget( + args[1], args[2]); + if (parsed == null) { + sender.sendMessage("§c无法识别"); + return true; + } + String areaName = parsed[0]; + String playerName = parsed[1]; + if (areaName.equalsIgnoreCase("global")) { + globalPlayerWhitelist.remove(playerName); + saveWhitelists(); + sender.sendMessage("§a已移除: " + + playerName); + } else { + Set wl = + areaPlayerWhitelist.get(areaName); + if (wl != null) { + wl.remove(playerName); + } + saveWhitelists(); + sender.sendMessage("§a已从 " + areaName + + " 移除: " + playerName); + } + return true; + } + sender.sendMessage("§c用法: /protect remove " + + "<区域/玩家> <区域/玩家>"); + return true; + } + + // ===== on/off/tempon 边框 ===== + if (sub.equals("on")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, false); + p.sendMessage("§a§l[防护] §f已显示 " + + ac.name + " 边框"); + return true; + } + if (sub.equals("off")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + cancelBorder((Player) sender); + ((Player) sender).sendMessage( + "§a§l[防护] §f已关闭边框显示"); + return true; + } + if (sub.equals("tempon")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + AreaConfig ac = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac == null) { + p.sendMessage("§c你不在任何防护区域内"); + return true; + } + showBorder(p, ac, true); + p.sendMessage("§a§l[防护] §f临时显示 " + + ac.name + " 边框(15秒)"); + return true; + } + + showHelp(sender); + return true; + } + + + private String[] parseAreaAndTarget(String arg1, String arg2) { + String r1 = resolveAreaName(arg1); + if (r1 != null) { + return new String[]{r1, arg2}; + } + String r2 = resolveAreaName(arg2); + if (r2 != null) { + return new String[]{r2, arg1}; + } + return null; + } + + private boolean playerExists(String playerName) { + // 在线玩家 + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.getName().equalsIgnoreCase(playerName)) return true; + } + // 离线玩家 + @SuppressWarnings("deprecation") + OfflinePlayer op = Bukkit.getOfflinePlayer(playerName); + return op.hasPlayedBefore(); + } + + + private boolean validateArea(CommandSender sender, String areaName) { + if (!areas.containsKey(areaName)) { + sender.sendMessage("§c区域不存在: " + areaName); + return false; + } + return true; + } + + + private String matname(Material mat) { + String name = mat.name(); + String[] parts = name.split("_"); + StringBuilder sb = new StringBuilder(); + for (String part : parts) { + if (sb.length() > 0) sb.append(" "); + sb.append(part.substring(0, 1).toUpperCase()) + .append(part.substring(1).toLowerCase()); + } + return sb.toString(); + } + private String yawToDir(float yaw) { + // MC中: 0=南, 90=西, 180=北, -90/270=东 + yaw = yaw % 360; + if (yaw < 0) yaw += 360; + if (yaw >= 315 || yaw < 45) return "南"; + if (yaw >= 45 && yaw < 135) return "西"; + if (yaw >= 135 && yaw < 225) return "北"; + return "东"; + } + private void saveAreaConfig(AreaConfig ac) { + File f = new File(rootDir, ac.name + ".txt"); + if (!f.exists()) return; + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + if (line.trim().startsWith("起点:")) { + lines.add("起点: " + ac.world + + "," + ac.x1 + "," + ac.z1); + } else if (line.trim().startsWith("终点:")) { + lines.add("终点: " + ac.world + + "," + ac.x2 + "," + ac.z2); + } else { + lines.add(line); + } + } + br.close(); + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + for (String l : lines) pw.println(l); + pw.close(); + } catch (IOException ignored) {} + } + + private void showHelp(CommandSender s) { + s.sendMessage("§e§l==== 区域防护 ===="); + s.sendMessage("§a/protect 工具 §7选地工具"); + s.sendMessage("§a/protect 创建 <名> §7创建区域"); + s.sendMessage("§a/protect 列表 §7区域列表"); + s.sendMessage("§a/protect 重载 §7重载配置"); + s.sendMessage("§a/protect 删除 <名> §7删除区域"); + s.sendMessage("§a/protect add [区域] <玩家> §7玩家加白"); + s.sendMessage("§a/protect remove [区域] <玩家> §7玩家删白"); + s.sendMessage("§a/protect additem [区域] <物品> §7物品加黑"); + s.sendMessage("§a/protect removeitem [区域] <物品> §7物品删黑"); + s.sendMessage("§a/protect on §7显示边框"); + s.sendMessage("§a/protect off §7关闭边框"); + s.sendMessage("§a/protect tempon §7临时边框(15秒)"); + s.sendMessage("§a/protect list §7列出全局白名单"); + s.sendMessage("§a/protect list <区域> §7列出区域白名单"); + s.sendMessage("§a/protect listitem §7列出全局物品黑名单"); + s.sendMessage("§a/protect listitem <区域> §7列出区域物品黑名单"); + + } +// ==================== 白名单管理公共方法 ==================== + + // ==================== 白名单操作 ==================== + + public boolean isPlayerGlobalWhitelisted( + String playerName) { + return globalPlayerWhitelist.contains(playerName); + } + + public boolean addPlayerToGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean isPlayerAreaWhitelisted( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + return wl != null && wl.contains(playerName); + } + + public boolean addPlayerToAreaWhitelist( + String areaName, String playerName) { + Set set = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap.newKeySet()); + boolean ok = set.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromAreaWhitelist( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + boolean ok = wl != null && wl.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + + + public int handleConfiscate(Player p, AreaConfig ac) { + if (isPlayerWhitelisted(p.getName(), ac)) return 0; + Set allItems = new HashSet<>(ac.confiscateItems); + allItems.addAll(globalItemBlacklist); + Set areaList = areaItemBlacklist.get(ac.name); + if (areaList != null) allItems.addAll(areaList); + + if (allItems.isEmpty()) return 0; + + int totalRemoved = 0; + String confiscatedNames = ""; + + for (String itemId : allItems) { + // 过滤掉区域名和无效ID + Material mat = Material.matchMaterial(itemId); + if (mat == null) { + plugin.getLogger().warning( + "[防护] 跳过无效物品: " + itemId); + continue; + } + ItemStack[] all = p.getInventory().getContents(); + int count = 0; + for (int i = 0; i < all.length; i++) { + if (all[i] != null && all[i].getType() == mat) { + count += all[i].getAmount(); + all[i] = null; + } + } + p.getInventory().setContents(all); + + ItemStack off = p.getInventory().getItemInOffHand(); + if (off != null && off.getType() == mat) { + count += off.getAmount(); + p.getInventory().setItemInOffHand(null); + } + + if (count > 0) { + totalRemoved += count; + if (!confiscatedNames.isEmpty()) + confiscatedNames += "§7、§e"; + confiscatedNames += "§e" + matname(mat) + + " x" + count; + } + } + + if (totalRemoved > 0) { + String msg = (ac.confiscateMsg != null + && !ac.confiscateMsg.isEmpty()) + ? ac.confiscateMsg + : "你携带了违禁品,已被没收"; + p.sendMessage("§c§l[区域防护] §f" + msg); + p.sendMessage("§7没收物品: " + confiscatedNames); + + if (ac.enableAnnounce + && ac.announceTemplate != null + && !ac.announceTemplate.isEmpty()) { + String ann = ac.announceTemplate + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)) + .replace("{items}", confiscatedNames); + Bukkit.broadcastMessage("§c§l[区域防护] §f" + ann); + } + for (String cmd : ac.punishCommands) { + String finalCmd = cmd + .replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)); + Bukkit.getScheduler().runTask(plugin, + () -> Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), finalCmd)); + } + } + return totalRemoved; + } + public void clearBlacklists() { + globalItemBlacklist.clear(); + areaItemBlacklist.clear(); + plugin.getLogger().info("[防护] 已清空所有黑名单"); + } + + @EventHandler + public void onEntityDamage( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑 ===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + + // 展示框被点击(旋转/放入/取出) + @EventHandler + public void onItemFrameInteract( + PlayerInteractAtEntityEvent e) { + Entity clicked = e.getRightClicked(); + String typeName = clicked.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + Player p = e.getPlayer(); + + // 全局白放行 + Set global = globalWhitelist.get("global"); + if (global != null + && global.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(clicked); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set areaWl = areaWhitelist.get(ac.name); + if (areaWl != null + && areaWl.contains(p.getName().toLowerCase())) { + return; + } + + // 非白名单 → 拦截 + // 记录当前旋转角度 + final Rotation savedRotation; + final Entity frame = clicked; + if (clicked instanceof org.bukkit.entity.ItemFrame) { + savedRotation = + ((org.bukkit.entity.ItemFrame) clicked) + .getRotation(); + } else { + savedRotation = null; + } + + e.setCancelled(true); + + // 下一tick强制恢复旋转 + if (savedRotation != null) { + final Rotation restore = savedRotation; + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (frame.isValid() + && frame instanceof org.bukkit.entity.ItemFrame) { + ((org.bukkit.entity.ItemFrame) frame) + .setRotation(restore); + } + }, 1L); + } + + p.sendMessage("§c§l[区域防护] §f禁止交互展示框"); + + // 驱逐远程敌对生物 + banRangedHostiles(p, ac); + } + + + + // 展示框保护:检查展示框本身+周围方块所属区域 + + // ===== 展示框攻击拦截(左键/投射物破坏)===== + @EventHandler + public void onEntityDamageByEntity( + org.bukkit.event.entity.EntityDamageByEntityEvent e) { + Entity entity = e.getEntity(); + String typeName = entity.getType().name(); + + if (typeName.equals("ITEM_FRAME") + || typeName.equals("GLOW_ITEM_FRAME")) { + // 展示框被攻击 + if (!(e.getDamager() instanceof Player)) return; + Player p = (Player) e.getDamager(); + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(p.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(entity); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(p.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + return; + } + + // ===== PVP 逻辑(原有的,不动)===== + if (!(e.getDamager() instanceof Player)) return; + if (!(e.getEntity() instanceof Player)) return; + Player p2 = (Player) e.getEntity(); + AreaConfig ac2 = getArea( + p2.getWorld().getName(), + p2.getLocation().getBlockX(), + p2.getLocation().getBlockY(), + p2.getLocation().getBlockZ()); + if (ac2 != null && ac2.denyPVP + && !isPlayerWhitelisted(p2.getName(), ac2)) { + e.setCancelled(true); + p2.sendMessage("§c§l[区域防护] §f禁止PVP"); + } + } + private AreaConfig findFrameArea(Entity frame) { + Location loc = frame.getLocation(); + int bx = loc.getBlockX(); + int by = loc.getBlockY(); + int bz = loc.getBlockZ(); + String world = loc.getWorld().getName(); + + AreaConfig ac = getArea(world, bx, by, bz); + if (ac != null && ac.denyItemFrame) return ac; + + int[][] offsets = { + {0, 0, 1}, {0, 0, -1}, + {1, 0, 0}, {-1, 0, 0}, + {0, 1, 0}, {0, -1, 0} + }; + for (int[] off : offsets) { + ac = getArea(world, + bx + off[0], by + off[1], bz + off[2]); + if (ac != null && ac.denyItemFrame) return ac; + } + return null; + } + + // ===== 投射物击中展示框(弓箭/弩)===== + @EventHandler + public void onProjectileHit( + org.bukkit.event.entity.ProjectileHitEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) return; + Player shooter = (Player) e.getEntity().getShooter(); + Entity hit = e.getHitEntity(); + if (hit == null) return; + String typeName = hit.getType().name(); + if (!typeName.equals("ITEM_FRAME") + && !typeName.equals("GLOW_ITEM_FRAME")) { + return; + } + + // 全局白放行 + Set gwl = globalWhitelist.get("global"); + if (gwl != null + && gwl.contains(shooter.getName().toLowerCase())) { + return; + } + + AreaConfig ac = findFrameArea(hit); + if (ac == null || !ac.denyItemFrame) return; + + // 区域白放行 + Set awl = areaWhitelist.get(ac.name); + if (awl != null + && awl.contains(shooter.getName().toLowerCase())) { + return; + } + + e.setCancelled(true); + shooter.sendMessage("§c§l[区域防护] §f禁止破坏展示框"); + } + + + // ===== 区域外火焰不能蔓延进区域 ===== + @EventHandler + public void onBlockSpread(BlockSpreadEvent e) { + Block block = e.getBlock(); + AreaConfig tgtArea = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (tgtArea != null && tgtArea.denyFireSpread) { + e.setCancelled(true); + } + } + + // ===== 区域内禁止点燃方块 ===== + @EventHandler + public void onIgnite(BlockIgniteEvent e) { + Player p = e.getPlayer(); + if (p == null) return; // 非玩家点燃的(闪电等) + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) { + e.setCancelled(true); + p.sendMessage("§c§l[区域防护] §f此区域禁止点燃物品"); + } + } + + // ===== 区域内方块着火(TNT、岩浆等)也阻止 ===== + @EventHandler + public void onBlockBurn(BlockBurnEvent e) { + Block block = e.getBlock(); + AreaConfig ac = getArea( + block.getWorld().getName(), + block.getX(), block.getY(), block.getZ()); + if (ac != null && ac.denyFireSpread) + e.setCancelled(true); + } + + + private void savePeaceWhitelist(AreaConfig ac) { + File file = new File(rootDir, ac.name + ".txt"); + if (!file.exists()) return; + + try { + List lines = new ArrayList<>(); + BufferedReader reader = new BufferedReader( + new FileReader(file, StandardCharsets.UTF_8)); + String line; + boolean found = false; + + while ((line = reader.readLine()) != null) { + String trimmed = line.trim(); + if (trimmed.contains("和平") + && trimmed.contains("白名单")) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + found = true; + } else { + lines.add(line); + } + } + reader.close(); + + if (!found && !ac.peaceWhitelist.isEmpty()) { + StringBuilder sb = new StringBuilder(); + sb.append("和平模式白名单:"); + boolean first = true; + for (String name : ac.peaceWhitelist) { + if (!first) sb.append(","); + sb.append(name); + first = false; + } + lines.add(sb.toString()); + } + + FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8); + for (String l : lines) { + writer.write(l + "\n"); + } + writer.close(); + } catch (IOException e) { + plugin.getLogger().warning( + "[防护] 保存和平白名单失败: " + e.getMessage()); + } + } + + + @EventHandler + public void onPaintingPlace( + org.bukkit.event.player.PlayerBucketEmptyEvent e) { + // 暂不处理 + } + + + // ==================== 数据类 ==================== + + public static class AreaConfig { + public String name = ""; + public String world = ""; + public int x1, z1, x2, z2; + public int yMin = 0, yMax = 255; + public List confiscateItems = new ArrayList<>(); + public List denyUseItems = new ArrayList<>(); + public List clearEffects = new ArrayList<>(); // 清除单个效果名 + public boolean clearAllBadEffects = false; // 清除所有负面+中性 + public List giveEffects = new ArrayList<>(); // [效果名, 等级, 秒数] + public List punishCommands = new ArrayList<>(); + public boolean denyBlockPlace = false; + public boolean denyBlockBreak = false; + public boolean denyPVP = false; + public boolean denyFallDamage = false; + public boolean denyHunger = false; + public boolean denyAllDamage = false; + public boolean denyMount = false; + public boolean denyDrop = false; + public boolean denyEnderPearl = false; + public boolean denyBow = false; + public boolean denyPotion = false; + public boolean denyExplosion = false; + public boolean enableAnnounce = false; + public String announceTemplate = ""; + public String confiscateMsg = ""; + public String enterMsg = ""; + public String leaveMsg = ""; + public boolean denyRaid = false; + public boolean denyFireSpread = false; + public boolean denyAllEffects = false;; + public boolean peaceMode = false; + public boolean denyItemFrame = false; + public Set peaceWhitelist = new HashSet<>(); + public String enforceGameMode = null; // 强制游戏模式 + public Set modeExempt = new HashSet<>(); // 模式排除名单 + + + + + private static final Map EFFECT_MAP + = new HashMap<>(); + static { + // 负面效果 + EFFECT_MAP.put("缓慢", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("凋零", PotionEffectType.WITHER); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("霉运", PotionEffectType.UNLUCK); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("蓄风", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生", PotionEffectType.INFESTED); + // 中性效果 + EFFECT_MAP.put("不祥之兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("不祥征兆", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("袭击征兆", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("试炼征兆", PotionEffectType.TRIAL_OMEN); + // 正面效果 + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); + EFFECT_MAP.put("瞬间治疗", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("跳跃提升", PotionEffectType.JUMP_BOOST); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性提升", PotionEffectType.RESISTANCE); + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("生命提升", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("村庄英雄", PotionEffectType.HERO_OF_THE_VILLAGE); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); + EFFECT_MAP.put("潮涌能量", PotionEffectType.CONDUIT_POWER); + EFFECT_MAP.put("海豚的恩惠", PotionEffectType.DOLPHINS_GRACE); + + + // 带效果二字 + // 补充带"效果"后缀的别名 + EFFECT_MAP.put("黑暗效果", PotionEffectType.DARKNESS); + EFFECT_MAP.put("不祥之兆效果", PotionEffectType.BAD_OMEN); + EFFECT_MAP.put("袭击之兆效果", PotionEffectType.RAID_OMEN); + EFFECT_MAP.put("试炼之兆效果", PotionEffectType.TRIAL_OMEN); + EFFECT_MAP.put("缓慢效果", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("中毒效果", PotionEffectType.POISON); + EFFECT_MAP.put("凋零效果", PotionEffectType.WITHER); + EFFECT_MAP.put("失明效果", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("饥饿效果", PotionEffectType.HUNGER); + EFFECT_MAP.put("虚弱效果", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("反胃效果", PotionEffectType.NAUSEA); + EFFECT_MAP.put("飘浮效果", PotionEffectType.LEVITATION); + EFFECT_MAP.put("发光效果", PotionEffectType.GLOWING); + EFFECT_MAP.put("霉运效果", PotionEffectType.UNLUCK); + EFFECT_MAP.put("蓄风效果", PotionEffectType.WIND_CHARGED); + EFFECT_MAP.put("盘丝效果", PotionEffectType.WEAVING); + EFFECT_MAP.put("渗浆效果", PotionEffectType.OOZING); + EFFECT_MAP.put("寄生效果", PotionEffectType.INFESTED); + EFFECT_MAP.put("挖掘疲劳效果", PotionEffectType.MINING_FATIGUE); + EFFECT_MAP.put("瞬间伤害效果", PotionEffectType.INSTANT_DAMAGE); + EFFECT_MAP.put("村庄英雄效果", PotionEffectType.HERO_OF_THE_VILLAGE); + } + + + public int ruleCount() { + int c = 0; + if (!confiscateItems.isEmpty()) c++; + if (denyBlockPlace) c++; + if (denyBlockBreak) c++; + if (denyPVP) c++; + if (denyFallDamage) c++; + if (denyHunger) c++; + if (denyAllDamage) c++; + if (denyDrop) c++; + if (denyMount) c++; + if (denyEnderPearl) c++; + if (denyBow) c++; + if (denyRaid) c++; + if (denyPotion) c++; + if (denyExplosion) c++; + if (denyFireSpread) c++; + if (denyAllEffects) c++; + if (!clearEffects.isEmpty()) c++; + if (clearAllBadEffects) c++; + if (!giveEffects.isEmpty()) c++; + if (enableAnnounce) c++; + if (!punishCommands.isEmpty()) c++; + if (!denyUseItems.isEmpty()) c++; + if (peaceMode) c++; + if (denyItemFrame) c++; + if (!peaceWhitelist.isEmpty()) c++; + + return c; + } + } + } \ No newline at end of file diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondManager.java new file mode 100644 index 0000000..ea78152 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondManager.java @@ -0,0 +1,697 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.io.File; +import java.sql.*; +import java.util.*; + +public class BondManager { + + private final Main plugin; + private Connection db; + + // ===== 交易类型常量 ===== + public static final String TX_ADMIN_GIVE = "admin_give"; + public static final String TX_ADMIN_DEDUCT = "admin_deduct"; + public static final String TX_TRANSFER_OUT = "transfer_out"; + public static final String TX_TRANSFER_IN = "transfer_in"; + public static final String TX_REDEEM = "redeem"; + public static final String TX_SHOP_BUY = "shop_buy"; + public static final String TX_DAILY_SIGN = "daily_sign"; + public static final String TX_FREEZE = "freeze"; + public static final String TX_UNFREEZE = "unfreeze"; + + + public static final int MAX_WATER_DAYS = 14; + public static final int ADMIN_MAX_WATER_DAYS = 30; + + public BondManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + // ======================== 数据库初始化 ======================== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 债券余额(含冻结状态) + st.execute("CREATE TABLE IF NOT EXISTS bonds (" + + "player_name TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "status TEXT DEFAULT 'normal')"); + + // 兼容旧表 + try { + st.execute("ALTER TABLE bonds " + + "ADD COLUMN status " + + "TEXT DEFAULT 'normal'"); + } catch (SQLException ignored) {} + + // 旧流水(保留兼容) + st.execute("CREATE TABLE IF NOT EXISTS bond_log (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "reason TEXT DEFAULT ''," + + "time INTEGER DEFAULT 0)"); + + // 新交易流水表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "bond_transaction (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "type TEXT NOT NULL," + + "amount INTEGER NOT NULL," + + "target_player TEXT DEFAULT ''," + + "operator TEXT DEFAULT ''," + + "reason TEXT DEFAULT ''," + + "balance_after INTEGER DEFAULT 0," + + "time INTEGER DEFAULT 0)"); + + try { + st.execute("CREATE INDEX " + + "IF NOT EXISTS idx_tx_pt " + + "ON bond_transaction(" + + "player_name, time)"); + } catch (SQLException ignored) {} + try { + st.execute("ALTER TABLE bond_transaction " + + "ADD COLUMN balance_before " + + "INTEGER DEFAULT 0"); + } catch (SQLException ignored) {} + + + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[Bond] DB init failed: " + + e.getMessage(), e); + } + } + + // ======================== 账户状态 ======================== + + public boolean isFrozen(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return "frozen".equals(s); + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return false; + } + + public String getAccountStatus(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT status FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String s = rs.getString("status"); + rs.close(); ps.close(); + return s != null ? s : "normal"; + } + rs.close(); ps.close(); + } catch (SQLException ignored) {} + return "normal"; + } + + public void freezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='frozen' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + public void unfreezeAccount(String player) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET status='normal' " + + "WHERE player_name=?"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + } + + private void ensureAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR IGNORE INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,0,'normal')"); + ps.setString(1, player); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + // ======================== 余额查询 ======================== + + public int getBonds(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount FROM bonds " + + "WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + int v = 0; + if (rs.next()) v = rs.getInt("amount"); + rs.close(); ps.close(); + return v; + } catch (SQLException e) { return 0; } + } + + /** 该玩家是否在 bond 表中有真实记录 */ + public boolean hasBondAccount(String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM bonds " + + "WHERE player_name=? " + + "AND amount > 0"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + boolean exists = rs.next(); + rs.close(); ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ======================== 增加债券 ======================== + + public int addBonds(String player, int amount) { + return addBondsInternal(player, amount, + TX_ADMIN_GIVE, "", "", + "管理员发放"); + } + + public int addBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return -1; + return addBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + private int addBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + int after = before + amount; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status)" + + " VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, amount); + return after; + } + + // ======================== 扣除债券 ======================== + + + + private boolean deductBondsInternal(String player, + int amount, + String type, + String targetPlayer, + String operator, + String reason) { + int before = getBonds(player); + if (before < amount) return false; + int after = before - amount; + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE bonds SET amount=? " + + "WHERE player_name=?"); + ps.setInt(1, after); + ps.setString(2, player); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + logTransaction(player, type, amount, + targetPlayer, operator, reason, + before, after); + logLegacy(player, -amount); + return true; + } + + // ======================== 流水写入 ======================== + + private void logTransaction(String player, + String type, + int amount, + String targetPlayer, + String operator, + String reason, + int balanceBefore, + int balanceAfter) { + try { + // 合并模式:shop_buy 30秒内同商品合并 + if ("shop_buy".equals(type) + && reason != null + && !reason.isEmpty()) { + long mergeWindow = + System.currentTimeMillis() - 30000L; + PreparedStatement chk = + db.prepareStatement( + "SELECT id, amount " + + "FROM bond_transaction " + + "WHERE player_name=? " + + "AND type=? " + + "AND reason=? " + + "AND time > ? " + + "ORDER BY time DESC " + + "LIMIT 1"); + chk.setString(1, player); + chk.setString(2, type); + chk.setString(3, reason); + chk.setLong(4, mergeWindow); + ResultSet rs = chk.executeQuery(); + if (rs.next()) { + int existId = rs.getInt("id"); + int existAmt = rs.getInt("amount"); + rs.close(); + chk.close(); + PreparedStatement upd = + db.prepareStatement( + "UPDATE bond_transaction " + + "SET amount=?, " + + "balance_after=?, " + + "time=? " + + "WHERE id=?"); + upd.setInt(1, existAmt + amount); + upd.setInt(2, balanceAfter); + upd.setLong(3, + System.currentTimeMillis()); + upd.setInt(4, existId); + upd.executeUpdate(); + upd.close(); + return; + } + rs.close(); + chk.close(); + } + + // 原始插入 + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_transaction " + + "(player_name, type, amount," + + " target_player, operator," + + " reason, balance_before," + + " balance_after, time)" + + " VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, type); + ps.setInt(3, amount); + ps.setString(4, + targetPlayer != null ? targetPlayer : ""); + ps.setString(5, + operator != null ? operator : ""); + ps.setString(6, + reason != null ? reason : ""); + ps.setInt(7, balanceBefore); + ps.setInt(8, balanceAfter); + ps.setLong(9, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + // ======================== 扣除债券 ======================== + + /** 完整版:含冻结检查 + 流水记录 */ + public boolean deductBonds(String player, int amount, + String type, + String targetPlayer, + String operator, + String reason) { + if (isFrozen(player)) return false; + return deductBondsInternal(player, amount, + type, targetPlayer, operator, reason); + } + + public boolean checkoutCart(String player, + String[] itemNames, + int[] subtotals) { + int total = 0; + for (int s : subtotals) total += s; + if (isFrozen(player)) return false; + + int before = getBonds(player); + if (before < total) return false; + int after = before - total; + + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?,'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, after); + ps.setInt(3, after); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + + int running = before; + for (int i = 0; i < itemNames.length; i++) { + int next = running - subtotals[i]; + logTransaction(player, "shop_buy", subtotals[i], + "", "商店系统", + "购物车购买-" + itemNames[i], + running, next); + running = next; + } + + logLegacy(player, -total); + return true; + } + + public boolean setBonds(String player, int amount) { + ensureAccount(player); + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bonds " + + "(player_name,amount,status) " + + "VALUES (?,?, 'normal') " + + "ON CONFLICT(player_name) " + + "DO UPDATE SET amount=?"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setInt(3, amount); + ps.executeUpdate(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return true; + } + + // ======================== 转账 ======================== + + public TransferResult transfer(String from, + String to, + int amount) { + if (from.equalsIgnoreCase(to)) + return new TransferResult(false, + "不能给自己转账"); + if (isFrozen(from)) + return new TransferResult(false, + "你的账户已被冻结"); + if (isFrozen(to)) + return new TransferResult(false, + to + " 的账户已被冻结"); + if (isSameIP(from, to)) + return new TransferResult(false, + "不能与同IP玩家转账"); + + if (!deductBondsInternal(from, amount, + TX_TRANSFER_OUT, to, from, + "转账给" + to)) + return new TransferResult(false, "债券不足"); + + addBondsInternal(to, amount, + TX_TRANSFER_IN, from, from, + "收到来自" + from + "的转账"); + + return new TransferResult(true, "转账成功"); + } + + // ======================== 同IP检测 ======================== + + public boolean isSameIP(String p1, String p2) { + String ip1 = getPlayerIPAny(p1); + String ip2 = getPlayerIPAny(p2); + if (ip1 == null || ip2 == null) return false; + return ip1.equals(ip2); + } + + /** 获取同IP下的所有其他在线玩家 */ + public List getSameIPPlayers(String player) { + String ip = getPlayerIPAny(player); + if (ip == null) return Collections.emptyList(); + List list = new ArrayList<>(); + for (Player p : + plugin.getServer().getOnlinePlayers()) { + String pIp = plugin.getPlayerIP(p); + if (pIp != null && pIp.equals(ip) + && !p.getName() + .equalsIgnoreCase(player)) { + list.add(p.getName()); + } + } + return list; + } + + private String getPlayerIPAny(String player) { + // 在线:直接取 + Player p = plugin.getServer() + .getPlayerExact(player); + if (p != null) { + String ip = plugin.getPlayerIP(p); + if (ip != null) return ip; + } + // 离线:查主库 + try { + Object v = plugin.getDb() + .getField(player, "last_login_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + try { + Object v = plugin.getDb() + .getField(player, "register_ip"); + if (v != null) return v.toString(); + } catch (Exception ignored) {} + return null; + } + + // ======================== 流水查询 ======================== + + public List> getTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } +// 放在 getTransactions 方法后面: + + /** + * 获取该玩家全部流水(不限时间,供打印使用) + */ + public List> + getAllTransactions(String player) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "ORDER BY time ASC"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("player_name", + rs.getString("player_name")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("operator", + rs.getString("operator")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } +// 放在 getAllTransactions 方法后面: + + /** + * 获取所有有流水记录的玩家名(去重) + */ + public List getAllPlayerNames() { + List list = new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT DISTINCT player_name " + + "FROM bond_transaction " + + "ORDER BY player_name ASC"); + while (rs.next()) { + list.add(rs.getString("player_name")); + } + rs.close(); st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTransferTransactions( + String player, int days) { + days = Math.min(days, MAX_WATER_DAYS); + long cutoff = System.currentTimeMillis() + - (long) days * 24 * 60 * 60 * 1000; + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM bond_transaction " + + "WHERE player_name=? " + + "AND time>=? " + + "AND (type=? OR type=?) " + + "ORDER BY time DESC"); + ps.setString(1, player); + ps.setLong(2, cutoff); + ps.setString(3, TX_TRANSFER_OUT); + ps.setString(4, TX_TRANSFER_IN); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", rs.getInt("id")); + row.put("type", rs.getString("type")); + row.put("amount", + rs.getInt("amount")); + row.put("target_player", + rs.getString("target_player")); + row.put("reason", + rs.getString("reason")); + row.put("balance_after", + rs.getInt("balance_after")); + row.put("balance_before", rs.getInt("balance_before")); + row.put("time", rs.getLong("time")); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { e.printStackTrace(); } + return list; + } + + + private void logLegacy(String player, int amount) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO bond_log " + + "(player_name,amount,time) " + + "VALUES (?,?,?)"); + ps.setString(1, player); + ps.setInt(2, amount); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); ps.close(); + } catch (SQLException ignored) {} + } + + public Map getLog( + String player, int limit) { + Map info = new LinkedHashMap<>(); + info.put("balance", getBonds(player)); + return info; + } + + // ======================== 转账结果 ======================== + + public static class TransferResult { + public final boolean success; + public final String message; + public TransferResult(boolean success, + String message) { + this.success = success; + this.message = message; + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondPrinter.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondPrinter.java new file mode 100644 index 0000000..674ec3c --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/BondPrinter.java @@ -0,0 +1,378 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; + +public class BondPrinter { + + private final Main plugin; + private final File outputDir; + + private static final SimpleDateFormat FILE_SDF = + new SimpleDateFormat( + "yyyy-MM-dd_HH-mm-ss"); + private static final SimpleDateFormat CELL_SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + private static final LinkedHashMap + TYPE_NAMES = new LinkedHashMap<>(); + + static { + TYPE_NAMES.put("admin_give", "管理员发放"); + TYPE_NAMES.put("admin_deduct", "管理员扣除"); + TYPE_NAMES.put("transfer_out", "转出"); + TYPE_NAMES.put("transfer_in", "转入"); + TYPE_NAMES.put("redeem", "CDK兑换"); + TYPE_NAMES.put("shop_buy", "商城购买"); + TYPE_NAMES.put("shop_sell", "商城出售"); // ← 补上 + TYPE_NAMES.put("daily_sign", "签到奖励"); + TYPE_NAMES.put("freeze", "冻结"); + TYPE_NAMES.put("unfreeze", "解冻"); + + } + + public BondPrinter(Main plugin) { + this.plugin = plugin; + this.outputDir = new File( + plugin.getDataFolder(), "printer"); + if (!this.outputDir.exists()) { + this.outputDir.mkdirs(); + } + } + + // ============ 打印单个玩家 ============ + + public void printPlayer(String player, + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintPlayer( + player, sender)); + } + + private void doPrintPlayer(String player, + CommandSender sender) { + try { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + + String fileName = player + "_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + deleteOld(player); + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, + escXml(player)); + writeXmlRow(pw, + new String[]{ + "时间", "类型", + "金额", "对象", + "操作者", "理由", + "操作前余额", + "操作后余额"}); + + for (Map tx : txs) { + writeXmlRow(pw, new String[]{ + txTime(tx), + txTypeName(tx), + String.valueOf( + txInt(tx, + "amount")), + txStr(tx, + "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")) + }); + } + + writeXmlFooter(pw); + pw.flush(); + } finally { + pw.close(); + } + + if (sender != null) { + sender.sendMessage("§a[打印] §f" + + player + " §a流水已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + txs.size() + + " §7条,§724小时后删除"); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 打印全服 ============ + + public void printAll(CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doPrintAll(sender)); + } + + private void doPrintAll(CommandSender sender) { + try { + List players = + plugin.getBonds() + .getAllPlayerNames(); + if (players.isEmpty()) { + if (sender != null) { + sender.sendMessage("§c没有流水数据"); + } + return; + } + + String fileName = "全服流水_" + + FILE_SDF.format(new Date()) + + ".xls"; + File file = new File(outputDir, fileName); + + // ===== 收集所有流水 ===== + // key = 时间戳+玩家+金额,用于去重 + // value = 一行数据 + List allRows = new ArrayList<>(); + Set seenKeys = new HashSet<>(); + + for (String player : players) { + List> txs = + plugin.getBonds() + .getAllTransactions(player); + for (Map tx : txs) { + int txId = ((Number) tx.get("id")) + .intValue(); + String dedupKey = + String.valueOf(txId); + if (!seenKeys.add(dedupKey)) { + continue; + } + + String timeStr = txTime(tx); + long timeMs = 0; + Object t = tx.get("time"); + if (t != null) { + timeMs = + ((Number) t).longValue(); + } + + allRows.add(new String[]{ + player, + timeStr, + txTypeName(tx), + String.valueOf( + txInt(tx, "amount")), + txStr(tx, "target_player"), + txStr(tx, "operator"), + txStr(tx, "reason"), + String.valueOf( + txInt(tx, + "balance_before")), + String.valueOf( + txInt(tx, + "balance_after")), + // 额外存时间戳用于排序 + String.valueOf(timeMs) + }); + } + } + + // ===== 按时间戳排序 ===== + allRows.sort((a, b) -> { + long ta = Long.parseLong( + a[a.length - 1]); + long tb = Long.parseLong( + b[b.length - 1]); + return Long.compare(ta, tb); + }); + + // ===== 写入文件 ===== + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(file), + StandardCharsets.UTF_8)); + try { + writeXmlHeader(pw, "全服流水"); + writeXmlRow(pw, new String[]{ + "玩家", "时间", "类型", + "金额", "对象", "操作者", + "理由", "操作前余额", + "操作后余额" + }); + + for (String[] row : allRows) { + // 去掉最后的时间戳列 + String[] cells = + new String[row.length - 1]; + System.arraycopy(row, 0, + cells, 0, cells.length); + writeXmlRow(pw, cells); + } + + writeXmlFooter(pw); + pw.flush(); + + if (sender != null) { + sender.sendMessage( + "§a[打印] §f全服流水 §a已导出"); + sender.sendMessage("§7文件: §f" + + fileName); + sender.sendMessage("§7共 §f" + + players.size() + + " 名玩家,§f" + + allRows.size() + " §7条"); + sender.sendMessage( + "§7按时间从早到晚排序"); + sender.sendMessage( + "§724小时后自动删除"); + } + } finally { + pw.close(); + } + } catch (Exception e) { + if (sender != null) { + sender.sendMessage("§c导出失败: " + + e.getMessage()); + } + plugin.getLogger().severe( + "[Printer] " + e.getMessage()); + } + } + + // ============ 清理 ============ + + public void cleanupOldFiles() { + long now = System.currentTimeMillis(); + long maxAge = 24L * 60 * 60 * 1000; + File[] files = outputDir.listFiles( + (d, n) -> n.endsWith(".xls")); + if (files == null) { + return; + } + int count = 0; + for (File f : files) { + if (now - f.lastModified() > maxAge) { + if (f.delete()) { + count++; + } + } + } + if (count > 0) { + plugin.getLogger().info( + "[Printer] 清理 " + + count + " 个过期文件"); + } + } + + private void deleteOld(String player) { + File[] old = outputDir.listFiles( + (d, n) -> n.startsWith(player + "_") + && n.endsWith(".xls")); + if (old != null) { + for (File f : old) { + f.delete(); + } + } + } + + // ============ XML工具 ============ + + private void writeXmlHeader(PrintWriter pw, + String sheetName) { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(" "); + pw.println(" "); + } + + private void writeXmlRow(PrintWriter pw, + String[] cells) { + pw.println(" "); + for (String cell : cells) { + pw.println(" " + + "" + + escXml(cell != null ? cell : "") + + ""); + } + pw.println(" "); + } + + private void writeXmlFooter(PrintWriter pw) { + pw.println("
    "); + pw.println("
    "); + pw.println("
    "); + } + + private String escXml(String val) { + if (val == null) { + return ""; + } + return val.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """); + } + + private String txTime(Map tx) { + Object t = tx.get("time"); + if (t == null) return ""; + long ms = ((Number) t).longValue(); + return CELL_SDF.format(new Date(ms)); + } + + private String txTypeName( + Map tx) { + String type = (String) tx.get("type"); + return TYPE_NAMES.getOrDefault(type, type); + } + + private int txInt(Map tx, + String key) { + Object v = tx.get(key); + if (v == null) return 0; + return ((Number) v).intValue(); + } + + private String txStr(Map tx, + String key) { + Object v = tx.get(key); + return v != null ? v.toString() : ""; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/CDKManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CDKManager.java new file mode 100644 index 0000000..e07569f --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CDKManager.java @@ -0,0 +1,293 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.nio.charset.StandardCharsets; + +public class CDKManager { + + private final Main plugin; + private Connection db; + private final Map pendingInputs + = new ConcurrentHashMap<>(); + + public CDKManager(Main plugin) { + this.plugin = plugin; + initDB(); + } + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), "bond.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS cdk (" + + "code TEXT PRIMARY KEY," + + "amount INTEGER DEFAULT 0," + + "type TEXT DEFAULT 'bond'," + + "used INTEGER DEFAULT 0," + + "used_by TEXT DEFAULT ''," + + "created_time INTEGER DEFAULT 0," + + "used_time INTEGER DEFAULT 0)"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[CDK] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== CDK兑换(供SDF1调用) ===== + + /** + * 兑换CDK,返回结果字符串 + * @return "success:金额:余额前:余额后" + * 或 "fail:原因" + */ + public String redeem(String code, String player) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT amount,type,used FROM cdk " + + "WHERE code=?"); + ps.setString(1, code); + ResultSet rs = ps.executeQuery(); + if (!rs.next()) { + rs.close(); ps.close(); + return "fail:not_found"; + } + int amount = rs.getInt("amount"); + String type = rs.getString("type"); + int used = rs.getInt("used"); + rs.close(); ps.close(); + + if (used == 1) + return "fail:already_used"; + + if (plugin.getBonds() + .isFrozen(player)) + return "fail:frozen"; + + PreparedStatement up = db.prepareStatement( + "UPDATE cdk SET used=1," + + "used_by=?,used_time=? " + + "WHERE code=?"); + up.setString(1, player); + up.setLong(2, + System.currentTimeMillis()); + up.setString(3, code); + up.executeUpdate(); up.close(); + + if ("bond".equals(type)) { + int bef = plugin.getBonds() + .getBonds(player); + plugin.getBonds().addBonds(player, + amount, + BondManager.TX_REDEEM, + "", "CDK系统", + "CDK兑换: " + code); + int aft = plugin.getBonds() + .getBonds(player); + return "success:" + amount + + ":" + bef + ":" + aft; + } + return "fail:unknown_type"; + } catch (SQLException e) { + return "fail:" + e.getMessage(); + } + } + + // ===== 聊天输入系统 ===== + + public void requestInput(Player p, + String type, + String context) { + CDKInput input = new CDKInput(); + input.type = type; + input.context = context; + input.time = System.currentTimeMillis(); + pendingInputs.put(p.getName(), input); + } + + public boolean isListening(String playerName) { + return pendingInputs + .containsKey(playerName); + } + + public void onChat(Player p, String msg) { + CDKInput input = pendingInputs + .remove(p.getName()); + if (input == null) return; + + if (System.currentTimeMillis() + - input.time > 60000) { + p.sendMessage("§c输入超时"); + return; + } + + if ("cdk".equals(input.type)) { + String result = redeem( + msg.trim(), p.getName()); + if (result != null + && result.startsWith("success:")) { + String[] parts = result.split(":"); + int amt = Integer.parseInt(parts[1]); + int bef = Integer.parseInt(parts[2]); + int aft = Integer.parseInt(parts[3]); + p.sendMessage("§e§l[CDK] §f兑换成功!" + + "获得 §c" + amt + "§f债券"); + p.sendMessage("§7余额: §e" + bef + + " §7-> §a" + aft); + } else { + p.sendMessage("§c§l[CDK] §f" + + (result != null + ? result.replace("fail:", "") + : "无效的兑换码")); + } + return; + } + + if ("econ".equals(input.type)) { + handleEconInput(p, msg.trim(), + input.context); + return; + } + + if ("bond".equals(input.type)) { + handleBondInput(p, msg.trim(), + input.context); + } + } + + private void handleEconInput(Player admin, + String msg, + String target) { + net.milkbowl.vault.economy.Economy econ + = null; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy + .Economy.class); + if (reg != null) + econ = reg.getProvider(); + } catch (Exception ignored) {} + if (econ == null) { + admin.sendMessage("§c经济系统不可用"); + return; + } + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String numStr = + msg.substring(1).trim(); + double amount = + Double.parseDouble(numStr); + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + if (msg.startsWith("-")) { + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp == null) { + admin.sendMessage("§c" + target + + " 不在线"); + return; + } + if (!econ.has(tp, amount)) { + admin.sendMessage("§c余额不足"); + return; + } + econ.withdrawPlayer(tp, amount); + admin.sendMessage("§a扣款成功"); + } else { + econ.depositPlayer(target, amount); + admin.sendMessage("§a到账成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + private void handleBondInput(Player admin, + String msg, + String target) { + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String sign = msg.substring(0, 1); + String rest = + msg.substring(1).trim(); + String[] parts = + rest.split("\\s+", 2); + int amount = Integer.parseInt(parts[0]); + String reason = parts.length >= 2 + ? parts[1].trim() + : "管理员手动调整"; + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + String opName = admin.getName(); + String txType = "-".equals(sign) + ? BondManager.TX_ADMIN_DEDUCT + : BondManager.TX_ADMIN_GIVE; + + if ("-".equals(sign)) { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + if (plugin.getBonds().deductBonds( + target, amount, txType, + "", opName, reason)) { + admin.sendMessage("§a扣除成功"); + } else { + admin.sendMessage("§c债券不足"); + } + } else { + if (plugin.getBonds() + .isFrozen(target)) { + admin.sendMessage("§c账户冻结"); + return; + } + plugin.getBonds().addBonds(target, + amount, txType, + "", opName, reason); + admin.sendMessage("§a发放成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + + public void cancelInput(String player) { + pendingInputs.remove(player); + } + + static class CDKInput { + String type; + String context; + long time; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatFilterManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatFilterManager.java new file mode 100644 index 0000000..59cb5de --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatFilterManager.java @@ -0,0 +1,612 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.*; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatFilterManager { + + private final JavaPlugin plugin; + private final List whitelistUrls = + new ArrayList<>(); + private final Set whitelistPlayers = + new TreeSet<>( + String.CASE_INSENSITIVE_ORDER); + private final Map + punishmentRules = + new LinkedHashMap<>(); + private final Map + punishmentDurations = + new LinkedHashMap<>(); + private final Map + violationCount = + new ConcurrentHashMap<>(); + private final Map + mutedPlayers = + new ConcurrentHashMap<>(); + private final Map messages = + new LinkedHashMap<>(); + private boolean notifyAdmin = true; + private boolean notifyAll = false; + private int muteDuration = 300; + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean v) { + enabled = v; + } + + + private static final Pattern URL_PATTERN = + Pattern.compile( + "(?i)(?:https?://\\S+)" + + "|(?:(?:[a-zA-Z0-9]" + + "(?:[a-zA-Z0-9\\-]*" + + "[a-zA-Z0-9])?\\.)+" + + "[a-zA-Z]{2,})" + + "(?:/\\S*)?"); + private static final Pattern DOT_VARIANTS = + Pattern.compile( + "[\uff0e\u3002\u2025\u2026\u00b7]"); + private static final Pattern COLOR_CODES = + Pattern.compile( + "\u00a7[0-9a-fk-orA-FK-OR]"); + + private static final Set FILE_EXT; + static { + Set s = new HashSet<>( + Arrays.asList( + "txt", "yml", "yaml", + "json", "xml", "java", + "class", "jar", "png", + "jpg", "jpeg", "gif", + "bmp", "svg", "ico", + "webp", "mp3", "mp4", + "avi", "mkv", "wav", + "flac", "ogg", "zip", + "rar", "7z", "tar", + "gz", "cfg", "conf", + "ini", "log", "db", + "sql", "sh", "bat", + "html", "htm", "css", + "js", "md", "pdf", + "dll", "so", "exe", + "bin", "c", "cpp", + "py", "rb", "go", + "rs", "kt", "lua", + "php", "asp", "jsp")); + FILE_EXT = Collections.unmodifiableSet(s); + } + + public ChatFilterManager(JavaPlugin plugin) { + this.plugin = plugin; + } + + // ========== 配置加载 ========== + + public void loadConfig() { + whitelistUrls.clear(); + whitelistPlayers.clear(); + punishmentRules.clear(); + punishmentDurations.clear(); + messages.clear(); + + File f = new File( + plugin.getDataFolder(), "chat.txt"); + if (!f.exists()) writeDefaultFile(f); + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("白名单") + || t.equals("白名单:")) { + section = "whitelist"; + continue; + } + if (t.equals("处罚规则") + || t.equals("处罚规则:")) { + section = "punishment"; + continue; + } + if (t.equals("白名单玩家") + || t.equals("白名单玩家:")) { + section = "players"; + continue; + } + if (t.contains(":")) { + String[] kv = t.split(":", 2); + String k = kv[0].trim(); + String v = kv[1].trim(); + if (equals("启用过滤") + || equals("enabled")) { + enabled = parseBool(v); + continue; + } + + if (k.equals("通知管理员")) { + notifyAdmin = parseBool(v); + continue; + } + if (k.equals("全服通报")) { + notifyAll = parseBool(v); + continue; + } + if (k.equals("禁言时长")) { + try { + muteDuration = + Integer.parseInt(v); + } catch (Exception ignored) { + } + continue; + } + } + switch (section) { + case "whitelist": + whitelistUrls.add( + t.toLowerCase()); + break; + case "punishment": + String[] parts = t.split(":"); + if (parts.length >= 2) { + try { + int cnt = + Integer.parseInt( + parts[0] + .trim()); + String type = + parts[1].trim() + .toLowerCase(); + int dur = + parts.length >= 3 + ? Integer.parseInt( + parts[2] + .trim()) + : muteDuration; + punishmentRules.put( + cnt, type); + punishmentDurations.put( + cnt, dur); + } catch (Exception ignored) { + } + } + break; + case "players": + whitelistPlayers.add(t); + break; + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[Sdf1_chat] 配置加载失败: " + + e.getMessage()); + } + + // 从消息.txt读取chat_开头的消息 + loadMessages(); + } + + private void loadMessages() { + File msgFile = new File( + plugin.getDataFolder(), + "消息.txt"); + if (!msgFile.exists()) return; + try (BufferedReader mr = + new BufferedReader( + new InputStreamReader( + new FileInputStream( + msgFile), + StandardCharsets.UTF_8))) { + String mline; + while ((mline = mr.readLine()) != null) { + mline = mline.trim(); + if (mline.isEmpty() + || mline.startsWith("#")) + continue; + // 支持 = 和 : 两种分隔符 + int eqIdx = mline.indexOf('='); + int coIdx = mline.indexOf(':'); + int splitIdx = -1; + if (eqIdx >= 0 && coIdx >= 0) { + splitIdx = Math.min(eqIdx, coIdx); + } else if (eqIdx >= 0) { + splitIdx = eqIdx; + } else if (coIdx >= 0) { + splitIdx = coIdx; + } + if (splitIdx < 0) continue; + String k = mline.substring(0, + splitIdx).trim(); + String v = mline.substring( + splitIdx + 1).trim(); + if (k.startsWith("chat_")) { + messages.put(k, v); + } + } + } catch (Exception ignored) { + } + } + + + private void writeDefaultFile(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("启用过滤: " + enabled); + pw.println("通知管理员: true"); + pw.println("全服通报: false"); + pw.println("禁言时长: 300"); + pw.println(); + pw.println("白名单:"); + pw.println("*.minecraft.net"); + pw.println("baidu.com"); + pw.println("github.com"); + pw.println(); + pw.println("处罚规则:"); + pw.println("1:warn:0"); + pw.println("2:warn:0"); + pw.println("3:mute:300"); + pw.println("5:mute:600"); + pw.println("8:kick:0"); + pw.println("10:ban:0"); + pw.println(); + pw.println("白名单玩家:"); + } catch (IOException ignored) { + } + } + + public void saveConfig() { + File f = new File( + plugin.getDataFolder(), "chat.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# Sdf1_chat 聊天过滤配置"); + pw.println("通知管理员: " + notifyAdmin); + pw.println("全服通报: " + notifyAll); + pw.println("禁言时长: " + muteDuration); + pw.println(); + pw.println("白名单:"); + for (String u : whitelistUrls) + pw.println(u); + pw.println(); + pw.println("处罚规则:"); + for (Map.Entry entry + : punishmentRules.entrySet()) { + int dur = punishmentDurations + .getOrDefault(entry.getKey(), + muteDuration); + pw.println(entry.getKey() + ":" + + entry.getValue() + + ":" + dur); + } + pw.println(); + pw.println("白名单玩家:"); + for (String p : whitelistPlayers) + pw.println(p); + } catch (IOException e) { + plugin.getLogger().severe( + "[Sdf1_chat] 保存失败: " + + e.getMessage()); + } + } + + // ========== 消息 ========== + + public String msg(String key, + String... args) { + String template = messages.getOrDefault( + key, key); + for (int i = 0; i < args.length - 1; + i += 2) { + template = template.replace( + "{" + args[i] + "}", + args[i + 1]); + } + return template; + } + + // ========== URL检测 ========== + + private String normalizeMessage(String msg) { + msg = COLOR_CODES.matcher(msg) + .replaceAll(""); + msg = DOT_VARIANTS.matcher(msg) + .replaceAll("."); + return msg; + } + + public List extractUrls( + String message) { + String normalized = + normalizeMessage(message); + List urls = new ArrayList<>(); + Matcher m = URL_PATTERN + .matcher(normalized); + while (m.find()) { + String url = m.group().toLowerCase(); + url = url.replaceAll( + "[\\s.,;!?,。;!?、)》」』】" + + "\\]\\[\\((]+$", + ""); + if (!url.isEmpty()) urls.add(url); + } + return urls; + } + + public boolean isWhitelisted(String url) { + String clean = url + .replaceAll("^https?://", "") + .toLowerCase(); + int colon = clean.indexOf(':'); + int slash = clean.indexOf('/'); + if (colon > 0 && (slash < 0 + || colon < slash)) { + clean = clean.substring(0, colon); + } + slash = clean.indexOf('/'); + if (slash > 0) { + clean = clean.substring(0, slash); + } + for (String entry : whitelistUrls) { + String e = entry.toLowerCase().trim(); + if (e.startsWith("*.")) { + String suffix = e.substring(1); + if (clean.endsWith(suffix) + || clean.equals( + suffix.substring(1))) + return true; + } else { + if (clean.equals(e) + || clean.endsWith( + "." + e)) + return true; + } + } + return false; + } + + public boolean isLikelyDomain(String url) { + int lastDot = url.lastIndexOf('.'); + if (lastDot < 0 + || lastDot >= url.length() - 1) + return false; + String ext = url.substring(lastDot + 1) + .toLowerCase(); + return !FILE_EXT.contains(ext); + } + + // ========== 玩家操作 ========== + + public boolean isMuted(String name) { + Long expiry = mutedPlayers.get(name); + if (expiry == null) return false; + if (System.currentTimeMillis() + >= expiry) { + mutedPlayers.remove(name); + return false; + } + return true; + } + + public String fmtDuration(int seconds) { + if (seconds <= 0) return "0秒"; + int day = seconds / 86400; + int hr = (seconds % 86400) / 3600; + int min = (seconds % 3600) / 60; + int sec = seconds % 60; + StringBuilder sb = new StringBuilder(); + if (day > 0) sb.append(day).append("天"); + if (hr > 0) + sb.append(hr).append("小时"); + if (min > 0) + sb.append(min).append("分"); + if (sec > 0 || sb.length() == 0) + sb.append(sec).append("秒"); + return sb.toString(); + } + + public void applyPunishment(Player player, + int violation) { + String type = "warn"; + int duration = muteDuration; + int maxRule = 0; + for (Map.Entry entry + : punishmentRules.entrySet()) { + if (violation >= entry.getKey() + && entry.getKey() > maxRule) { + maxRule = entry.getKey(); + type = entry.getValue(); + duration = punishmentDurations + .getOrDefault( + entry.getKey(), + muteDuration); + } + } + if (type.equals("mute")) { + if (isMuted(player.getName())) + return; + mutedPlayers.put(player.getName(), + System.currentTimeMillis() + + (long) duration + * 1000L); + } + final String fType = type; + final int fDur = duration; + final int fV = violation; + final String fName = player.getName(); + Bukkit.getScheduler().runTask(plugin, + () -> { + Player p = Bukkit.getPlayer(fName); + if (p == null) return; + switch (fType) { + case "warn": + // 警告不处罚,只提示 + break; + case "mute": + p.sendMessage( + msg("chat_muted")); + break; + case "kick": + p.kickPlayer( + msg("chat_muted")); + break; + case "ban": + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban " + fName + + " §c多次发送违规链接"); + break; + case "banip": + InetSocketAddress addr = + p.getAddress(); + if (addr != null) { + String ip = addr + .getAddress() + .getHostAddress(); + Bukkit.dispatchCommand( + Bukkit.getConsoleSender(), + "ban-ip " + ip + + " §c发送违规链接"); + } + break; + } + }); + } + + public void unmutePlayer(String name) { + mutedPlayers.remove(name); + } + + public void resetPlayer(String name) { + mutedPlayers.remove(name); + violationCount.remove(name); + } + + public boolean isPlayerWhitelisted( + String name) { + return whitelistPlayers.contains(name); + } + + public boolean isNotifyAdmin() { + return notifyAdmin; + } + + public boolean isNotifyAll() { + return notifyAll; + } + + public int getViolationCount(String name) { + return violationCount + .getOrDefault(name, 0); + } + + public void incrementViolation( + String name) { + violationCount.put(name, + getViolationCount(name) + 1); + } + + public List getWhitelistUrls() { + return whitelistUrls; + } + + public Set getWhitelistPlayers() { + return whitelistPlayers; + } + + public Map + getPunishmentRules() { + return punishmentRules; + } + + public Map + getPunishmentDurations() { + return punishmentDurations; + } + + public Map + getMutedPlayers() { + return mutedPlayers; + } + + public void setNotifyAdmin(boolean v) { + notifyAdmin = v; + } + + public void setNotifyAll(boolean v) { + notifyAll = v; + } + + public void setMuteDuration(int v) { + muteDuration = v; + } + + public void addUrl(String url) { + whitelistUrls.add( + url.toLowerCase().trim()); + saveConfig(); + } + + public boolean removeUrl(String url) { + boolean r = whitelistUrls.remove( + url.toLowerCase().trim()); + saveConfig(); + return r; + } + + public void addWhitelistPlayer( + String name) { + whitelistPlayers.add(name); + saveConfig(); + } + + public boolean removeWhitelistPlayer( + String name) { + boolean r = + whitelistPlayers.remove(name); + saveConfig(); + return r; + } + + public void cleanExpired() { + long now = System.currentTimeMillis(); + mutedPlayers.entrySet().removeIf( + e -> e.getValue() <= now); + } + + private boolean parseBool(String v) { + String s = v.toLowerCase().trim(); + return s.equals("true") + || s.equals("on") + || s.equals("yes") + || s.equals("1") + || s.contains("开启") + || s.contains("启用") + || s.contains("是"); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatInputManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatInputManager.java new file mode 100644 index 0000000..49f53fd --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ChatInputManager.java @@ -0,0 +1,842 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +public class ChatInputManager { + + public enum InputType { + NONE, + CHANGE_PWD_STEP1, + CHANGE_PWD_STEP2, + SET_EMAIL, + SET_EMAIL_VERIFY, + ADMIN_AUTH, + ADMIN_SET_POINTS, + ADMIN_SET_PWD, + ADMIN_DELETE_CONFIRM, + ADMIN_SET_AFK_TIME, + SMTP_CONFIG, + SMTP_VERIFY, + BACK_CHECKIN, + INVITE_INPUT_CODE, + TICKET_TITLE, + TICKET_DESC, + TICKET_SCORE, + TICKET_REJECT, + TICKET_ASSIGN, + TICKET_INVITE_PROVIDER, + TICKET_REMOVE_PROVIDER, + TICKET_COMPLETE, + TICKET_REPORT_ISSUE, + TICKET_REPLY, + TICKET_ADMIN_TYPE, + TICKET_ADMIN_TITLE, + TICKET_ADMIN_DESC, + TICKET_ADMIN_PROVIDER, + BACK_CHECK_ECONOMY_CONFIRM, + USER_MGMT_SEARCH, + + } + + public static class PlayerState { + public InputType type = InputType.NONE; + public String targetPlayer = ""; + public String ticketType = ""; + public String ticketTitle = ""; + public String ticketDesc = ""; + public int ticketId = 0; + public int smtpStep = 0; + public int scoreValue = 0; + + } + + private final Map states = + new HashMap<>(); + private Main plugin; + + public PlayerState getState(Player p) { + return states.computeIfAbsent( + p.getName(), + k -> new PlayerState()); + } + + public void reset(Player p) { + states.remove(p.getName()); + } + public boolean isInFlow(Player p) { + PlayerState state = states.get(p.getName()); + return state != null + && state.type != InputType.NONE; + } + + public void handleInput(Player p, String msg, + Main mainPlugin) { + if (this.plugin == null) + this.plugin = mainPlugin; + PlayerState state = getState(p); + ConfigManager config = + mainPlugin.getConfig2(); + DatabaseManager db = mainPlugin.getDb(); + + switch (state.type) { + + case CHANGE_PWD_STEP1: { + String name = p.getName(); + String salt = (String) db.getField( + name, "password_salt"); + String hash = + PasswordUtils.hash(msg, salt); + boolean mainOk = + db.checkPassword(name, hash); + if (mainOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "main"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + boolean tempOk = + db.checkPasswordOrTemp( + name, hash); + if (tempOk) { + state.type = + InputType.CHANGE_PWD_STEP2; + state.ticketTitle = "temp"; + p.sendMessage( + "§e请输入新密码:"); + return; + } + p.sendMessage(config.msg( + "password_wrong")); + return; + } + + case CHANGE_PWD_STEP2: { + String name = p.getName(); + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String oldHash = + (String) db.getField(name, + "password_hash"); + String oldSalt = + (String) db.getField(name, + "password_salt"); + String newSalt = + PasswordUtils.generateSalt(); + String newHash = + PasswordUtils.hash( + msg, newSalt); + if (newHash.equals(oldHash)) { + p.sendMessage(config.msg( + "password_same")); + reset(p); + return; + } + mainPlugin.recordPasswordChange( + name, oldHash, oldSalt); + db.setField(name, + "password_hash", newHash); + db.setField(name, + "password_salt", newSalt); + mainPlugin + .getNeedsPasswordChange() + .remove(name); + p.sendMessage(config.msg( + "password_changed")); + reset(p); + return; + } + + case SET_EMAIL: { + if (!msg.contains("@") + || !msg.contains(".")) { + p.sendMessage(config.msg( + "email_format_error")); + reset(p); + return; + } + String oldEmail = (String) + db.getField(p.getName(), + "email"); + String emailAddr = msg.trim(); + // 已绑定邮箱:验证登录密码 + if (oldEmail != null + && !oldEmail.isEmpty()) { + state.targetPlayer = emailAddr; + state.type = + InputType.SET_EMAIL_VERIFY; + state.ticketTitle = + "need_pwd"; + p.sendMessage( + "§e您已绑定邮箱: " + + oldEmail); + p.sendMessage( + "§e请验证登录密码以确认修改:"); + return; + } + // 未绑定邮箱:直接发验证码 + String code = String.valueOf( + (int) (Math.random() + * 900000 + + 100000)); + state.targetPlayer = emailAddr; + state.ticketTitle = code; + state.type = + InputType.SET_EMAIL_VERIFY; + final String to = emailAddr; + final String c = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + boolean sent = mainPlugin + .getEmail() + .sendVerifyCode( + to, c); + Bukkit.getScheduler() + .runTask(mainPlugin, + () -> { + if (sent) { + fp.sendMessage( + "§a验证码已发送到 " + + to); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败"); + reset(fp); + } + }); + }); + return; + } + + + case SET_EMAIL_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§c邮箱设置已取消"); + reset(p); + return; + } + String expected = + state.ticketTitle; + if (msg.trim().equals(expected)) { + db.setField(p.getName(), + "email", + state.targetPlayer); + p.sendMessage( + "§a邮箱已设置: " + + state.targetPlayer); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case ADMIN_AUTH: { + if (msg.equals( + config.adminPassword)) { + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openAdmin(p)); + } else { + p.sendMessage( + "§c管理密码错误"); + } + reset(p); + return; + } + + case ADMIN_SET_POINTS: { + try { + int pts = Integer.parseInt( + msg.trim()); + db.setField( + state.targetPlayer, + "points", pts); + p.sendMessage("§a已将 " + + state.targetPlayer + + " 积分设为 " + pts); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + String tgt = + state.targetPlayer; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getGui() + .openUserDetail( + p, tgt)); + reset(p); + return; + } + + case ADMIN_SET_PWD: { + if (!PasswordUtils.validate(msg)) { + p.sendMessage(config.msg( + "password_format_error")); + reset(p); + return; + } + String tgt = state.targetPlayer; + String salt = + PasswordUtils + .generateSalt(); + String hash = + PasswordUtils.hash( + msg, salt); + db.setField(tgt, + "temp_password", hash); + db.setField(tgt, "temp_pw_expire", + System.currentTimeMillis() + + 300000L); + db.setField(tgt, + "temp_pw_used", 0); + p.sendMessage("§a已为 " + tgt + + " 设置临时密码"); + Player tp = + Bukkit.getPlayer(tgt); + if (tp != null && tp.isOnline()) + tp.kickPlayer( + "§c管理员已设置临时密码," + + "请使用临时密码登录"); + // 邮件通知 + String emailAddr = (String) + db.getField(tgt, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String time = + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()); + String body = "玩家 " + tgt + + ":\n您的密码已于 " + + time + + " 被管理员重置为临时密码。\n" + + "如果是本人操作请忽略。\n" + + "如非本人操作请立即联系管理员。"; + final String to = emailAddr; + final String fBody = body; + final String fName = tgt; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> + mainPlugin.getEmail() + .sendTempPassword( + to, + fName, + fBody)); + } + reset(p); + return; + } + + case ADMIN_DELETE_CONFIRM: { + if (msg.equals( + state.targetPlayer)) { + db.deleteUser( + state.targetPlayer); + p.sendMessage("§a已删除 " + + state.targetPlayer); + Player tgt = + Bukkit.getPlayer( + state.targetPlayer); + if (tgt != null + && tgt.isOnline()) + tgt.kickPlayer( + "§c账号已被删除"); + } else { + p.sendMessage( + "§c输入不匹配,已取消"); + } + reset(p); + return; + } + + case ADMIN_SET_AFK_TIME: { + try { + int minutes = + Integer.parseInt( + msg.trim()); + config.afkTimeout = + minutes * 60; + config.saveSettings(); + p.sendMessage( + "§a挂机时长已设为 " + + minutes + + " 分钟"); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效数字"); + } + reset(p); + return; + } + + case SMTP_CONFIG: { + String m = msg.trim(); + if ("000".equals(m) + || "取消取消取消" + .equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if ("取消".equals(m)) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + int step = state.smtpStep; + if (step <= 1) { + if (!"0".equals(m)) + config.setSmtp( + "smtp地址", m); + state.smtpStep = 2; + p.sendMessage("§e当前端口: " + + config.getSmtp( + "smtp端口")); + p.sendMessage( + "§e输入端口(0跳过):"); + } else if (step == 2) { + if (!"0".equals(m)) + config.setSmtp( + "smtp端口", m); + state.smtpStep = 3; + p.sendMessage( + "§e输入发件邮箱:"); + } else if (step == 3) { + if (!"0".equals(m)) + config.setSmtp( + "smtp账号", m); + state.smtpStep = 4; + p.sendMessage( + "§e输入SMTP密码(0跳过):"); + } else if (step == 4) { + if (!"0".equals(m)) + config.setSmtp( + "smtp密码", m); + state.smtpStep = 5; + p.sendMessage( + "§e输入接收邮箱(留空发到发件邮箱):"); + } else if (step == 5) { + String recv = m; + if (recv.isEmpty() + || "0".equals(recv)) + recv = config.getSmtp( + "smtp账号"); + config.setSmtp( + "验证码接收邮箱", recv); + if (!config + .canSendSmtpVerify()) { + p.sendMessage( + "§c发送间隔不足1分钟"); + reset(p); + return; + } + String code = config + .generateSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String acct = config.getSmtp( + "smtp账号"); + if (acct.isEmpty() + || addr.isEmpty()) { + p.sendMessage( + "§c地址或账号为空"); + reset(p); + return; + } + config.recordSmtpSendTime(); + final String fRecv = recv; + final String fCode = code; + final Player fp = p; + Bukkit.getScheduler() + .runTaskAsynchronously( + mainPlugin, () -> { + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> fp.sendMessage( + "§e正在发送验证码...")); + boolean sent = + mainPlugin.getEmail() + .sendVerifyCode( + fRecv, + fCode); + final boolean fSent = sent; + Bukkit.getScheduler() + .runTask( + mainPlugin, + () -> { + if (fSent) { + state.type = + InputType + .SMTP_VERIFY; + state.smtpStep = 0; + fp.sendMessage( + "§a验证码已发送到 " + + fRecv); + fp.sendMessage( + "§e输入6位验证码(0取消):"); + } else { + fp.sendMessage( + "§c发送失败,查看控制台"); + reset(fp); + } + }); + }); + } + return; + } + + case SMTP_VERIFY: { + if ("0".equals(msg.trim()) + || "取消".equals( + msg.trim())) { + p.sendMessage( + "§cSMTP配置已取消"); + reset(p); + return; + } + if (config.checkSmtpVerifyCode( + msg.trim())) { + config.saveSmtp(); + config.clearSmtpVerifyCode(); + String addr = config.getSmtp( + "smtp地址"); + String port = config.getSmtp( + "smtp端口"); + String acct = config.getSmtp( + "smtp账号"); + String recv = config.getSmtp( + "验证码接收邮箱"); + boolean ssl = config.smtpSsl; + p.sendMessage("§a§l=== SMTP配置完成 ==="); + p.sendMessage("§7地址: §f" + addr); + p.sendMessage("§7端口: §f" + port); + p.sendMessage("§7账号: §f" + acct); + p.sendMessage("§7接收: §f" + recv); + p.sendMessage("§7加密: §f" + ssl); + p.sendMessage("§a§l===================="); + reset(p); + } else { + p.sendMessage( + "§c验证码错误(0取消):"); + } + return; + } + + case BACK_CHECKIN: { + String result = + mainPlugin.getCheckIn() + .backCheckIn(p, msg); + if (result == null) { + // 等待选择(NEED_CONFIRM) + return; + } + if (result.startsWith( + "NEED_CONFIRM:")) { + String[] parts = + result.split(":"); + // 存储到Main的Map + mainPlugin.pendingBackCheck.put( + p.getName(), + parts[1] + ":" + parts[2]); + return; + } + if (result.startsWith("§c")) { + p.sendMessage(result); + } else { + p.sendMessage(result); + } + reset(p); + return; + } + + + case INVITE_INPUT_CODE: { + String code = msg.trim(); + String myName = p.getName(); + String myCode = + (String) db.getField( + myName, + "invite_code"); + if (code.isEmpty()) { + p.sendMessage( + "§c邀请码不能为空"); + reset(p); + return; + } + if (code.equals(myCode)) { + p.sendMessage( + "§c不能用自己邀请码"); + reset(p); + return; + } + boolean found = false; + for (Map u : + db.getAllUsers()) { + String uc = (String) + u.getOrDefault( + "invite_code", ""); + if (code.equals(uc)) { + String inv = (String) + u.get("player_name"); + db.setField(myName, + "invited_by", inv); + db.addPoints(inv, 50); + p.sendMessage( + "§a邀请成功!邀请人: " + + inv); + Player ip = + Bukkit.getPlayer(inv); + if (ip != null + && ip.isOnline()) + ip.sendMessage("§a" + + myName + + " 使用了您的邀请码"); + found = true; + break; + } + } + if (!found) + p.sendMessage( + "§c邀请码不存在"); + reset(p); + return; + } + + case TICKET_TITLE: { + state.ticketTitle = msg; + state.type = + InputType.TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return; + } + + case TICKET_DESC: { + String tt = state.ticketType; + String tl = state.ticketTitle; + reset(p); + mainPlugin.getTicket() + .submitTicket( + p, tt, tl, msg); + return; + } + + case TICKET_SCORE: { + int tid = state.ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + reset(p); + mainPlugin.getTicket() + .confirmAndScore( + p, tid, sc); + } catch ( + NumberFormatException e) { + reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return; + } + + case TICKET_REJECT: { + int tid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .rejectTicket( + p, tid, msg); + return; + } + + case TICKET_ASSIGN: { + int tid = state.ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + reset(p); + mainPlugin.getTicket() + .assignToProvider( + p, tid, prov, + price); + final int fTid = tid; + Bukkit.getScheduler().runTask( + mainPlugin, () -> + mainPlugin + .getTicket() + .openDetail( + p, fTid)); + return; + } + + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return; + } + db.addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return; + } + + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + reset(p); + if (!db.isServiceProvider(tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return; + } + db.removeServiceProvider(tgt); + p.sendMessage("§a已移除 " + tgt); + return; + } + + case TICKET_COMPLETE: { + try { + int tid = Integer.parseInt( + msg.trim()); + mainPlugin.getTicket() + .completeAsProvider( + p, tid); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c请输入有效工单号"); + } + reset(p); + return; + } + + case TICKET_REPORT_ISSUE: { + String report = msg.trim(); + reset(p); + p.sendMessage("§e异常已上报"); + String tag = config.adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§c§l[工单异常] §f" + + p.getName() + + ": " + report); + } + } + return; + } + + case TICKET_REPLY: { + int replyTid = state.ticketId; + reset(p); + mainPlugin.getTicket() + .replyTicket( + p, replyTid, msg); + return; + } + + case TICKET_ADMIN_TYPE: { + state.ticketType = msg.trim(); + state.type = + InputType + .TICKET_ADMIN_TITLE; + p.sendMessage( + "§e请输入工单标题:"); + return; + } + + case TICKET_ADMIN_TITLE: { + state.ticketTitle = msg; + state.type = + InputType + .TICKET_ADMIN_DESC; + p.sendMessage( + "§e请输入工单描述:"); + return; + } + + case TICKET_ADMIN_DESC: { + state.targetPlayer = msg; + state.type = + InputType + .TICKET_ADMIN_PROVIDER; + p.sendMessage( + "§e输入服务商名和价格:"); + p.sendMessage( + "§7例: playerA 20"); + return; + } + + case TICKET_ADMIN_PROVIDER: { + String[] parts2 = msg.trim() + .split("\\s+"); + String prov2 = parts2[0]; + int price2 = 0; + if (parts2.length >= 2) { + try { + price2 = Integer.parseInt( + parts2[1]); + } catch ( + NumberFormatException e) { + } + } + String t2 = state.ticketType; + String ti2 = state.ticketTitle; + String d2 = state.targetPlayer; + reset(p); + mainPlugin.getTicket() + .adminCreateAndAssign( + p, t2, ti2, + d2, prov2, price2); + return; + } + + default: + reset(p); + return; + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/CheckInManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CheckInManager.java new file mode 100644 index 0000000..b594b0f --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CheckInManager.java @@ -0,0 +1,302 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +public class CheckInManager { + + private final Main plugin; + + public CheckInManager(Main plugin) { + this.plugin = plugin; + } + + // ===== 今日是否已签到 ===== + public boolean isCheckedInToday( + String name) { + String last = (String) plugin.getDb() + .getField(name, + "last_checkin_date"); + String today = new SimpleDateFormat( + "yyyy-MM-dd") + .format(new Date()); + return today.equals(last); + } + + // ===== 签到 ===== + // ===== 签到 ===== + public String checkIn(Player p) { + String name = p.getName(); + if (isCheckedInToday(name)) { + return plugin.getConfig2() + .msg("checkin_already"); + } + + Object streakObj = plugin.getDb() + .getField(name, "checkin_streak"); + int streak = streakObj != null + ? ((Number) streakObj).intValue() + : 0; + + String lastDate = (String) plugin.getDb() + .getField(name, "last_checkin_date"); + String today = new SimpleDateFormat("yyyy-MM-dd") + .format(new Date()); + + if (lastDate != null && !lastDate.isEmpty()) { + try { + java.util.Date lastD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(lastDate); + java.util.Date todayD = + new SimpleDateFormat("yyyy-MM-dd") + .parse(today); + long diff = todayD.getTime() + - lastD.getTime(); + if (diff > 86400000L * 2) { + streak = 0; + } else if (diff > 86400000L) { + streak++; + } + } catch (Exception ignored) { + } + } + + if (streak == 0) streak = 1; + else streak++; + + double multi = 1.0; + if (streak >= 21) multi = 1.5; + else if (streak >= 14) multi = 1.3; + else if (streak >= 7) multi = 1.1; + + int basePoints = + plugin.getConfig2().checkinPoints; + int points = (int) (basePoints * multi); + + plugin.getDb().addPoints(name, points); + plugin.getDb().setField(name, + "checkin_streak", streak); + plugin.getDb().setField(name, + "last_checkin_date", today); + + Object totalObj = plugin.getDb() + .getField(name, "total_checkin_days"); + int total = totalObj != null + ? ((Number) totalObj).intValue() + : 0; + plugin.getDb().setField(name, + "total_checkin_days", total + 1); + + // ★ 债券盲盒奖励(6参数版,写入流水)★ + int bondMin = + plugin.getConfig2().checkinBondMin; + int bondMax = + plugin.getConfig2().checkinBondMax; + if (bondMin <= 0) bondMin = 1; + if (bondMax < bondMin) bondMax = bondMin; + int bondReward = bondMin + + new java.util.Random() + .nextInt(bondMax - bondMin + 1); + plugin.getBonds().addBonds(name, bondReward, + "daily_sign", "", "签到系统", + "每日签到连续" + streak + "天"); + + return plugin.getConfig2() + .msg("checkin_success") + .replace("{points}", + String.valueOf(points)) + .replace("{streak}", + String.valueOf(streak)) + .replace("{multi}", + String.valueOf(multi)) + + " §e+" + bondReward + "债券"; + } + + // ===== 获取连续天数 ===== + public int getStreak(String name) { + Object val = plugin.getDb() + .getField(name, + "checkin_streak"); + if (val instanceof Number) { + return ((Number) val) + .intValue(); + } + return 0; + } + // 标准化日期 + private String normalizeDate(String input) { + String s = input.trim().replace("/", "-"); + if (s.matches("\\d{8}")) { + return s.substring(0, 4) + "-" + + s.substring(4, 6) + "-" + + s.substring(6, 8); + } + return s; + } + // ===== 补签入口 ===== + public String backCheckIn( + Player p, String dateStr) { + String name = p.getName(); + String normalized = + normalizeDate(dateStr); + + // 严格日期验证 + java.text.SimpleDateFormat sdf = + new java.text.SimpleDateFormat( + "yyyy-MM-dd"); + sdf.setLenient(false); + java.util.Date target; + try { + target = sdf.parse(normalized); + } catch (Exception ex) { + return "§c无效日期: " + dateStr; + } + String reformat = sdf.format(target); + if (!reformat.equals(normalized)) { + return "§c无效日期: " + dateStr; + } + + String today = sdf.format(new Date()); + if (normalized.compareTo(today) >= 0) { + return "§c今天不需要补签"; + } + + String threeDaysAgo = getPastDate(3); + if (normalized.compareTo( + threeDaysAgo) < 0) { + return "§c只能补签3天内" + + "(最多到 " + threeDaysAgo + ")"; + } + + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + boolean alreadyBacked = false; + if (!existing.isEmpty()) { + for (String d : existing.split(",")) { + if (normalizeDate(d.trim()) + .equals(normalized)) { + alreadyBacked = true; + break; + } + } + } + if (alreadyBacked) { + return "§c该日期已补签: " + normalized; + } + + Object normalCheck = + plugin.getDb().getField( + name, "last_checkin_date"); + String lastCheckin = normalCheck != null + ? normalCheck.toString() : ""; + if (lastCheckin.equals(normalized)) { + return "§c该日期已签到: " + normalized; + } + + // ★ 补签消耗:3债券 + 10积分 + // 积分不够时:用 9债券(3倍)代替 ★ + int bondCost = 3; + int pointCost = 10; + int bondAltCost = 9; // 3倍债券替代 + + int myBonds = plugin.getBonds() + .getBonds(name); + Object ptsObj = plugin.getDb() + .getField(name, "points"); + int myPoints = ptsObj != null + ? ((Number) ptsObj).intValue() + : 0; + + + // 方案A:3债券 + 10积分 + boolean canA = myBonds >= bondCost + && myPoints >= pointCost; + // 方案B:9债券(积分不够时) + boolean canB = myBonds >= bondAltCost + && myPoints < pointCost; + + if (!canA && !canB) { + if (myPoints < pointCost + && myBonds < bondAltCost) { + return "§c补签费用不足" + + "(需3债券+10积分," + + "或9债券代替)" + + " 当前: " + + myBonds + "债券 " + + myPoints + "积分"; + } + if (myBonds < bondCost) { + return "§c债券不足(需" + + bondCost + "枚)"; + } + return "§c积分不足(需" + + pointCost + "分)"; + } + + // 执行扣款 + if (canA) { + // 方案A:3债券 + 10积分 + plugin.getBonds().deductBonds( + name, bondCost, + "admin_deduct", "", + "补签系统", "补签扣款"); + plugin.getDb().deductPoints( + name, pointCost); + } else { + // 方案B:9债券代替 + plugin.getBonds().deductBonds( + name, bondAltCost, + "admin_deduct", "", + "补签系统", "补签扣款(债券替代)"); + } + + doBackCheckIn(p, normalized); + + String costMsg = canA + ? bondCost + "债券+" + pointCost + "积分" + : bondAltCost + "债券(替代)"; + return "§a补签成功!日期: " + + normalized + + " §7消耗: " + costMsg; + } + + + + // ===== 执行补签 ===== + // ===== 执行补签 ===== + public void doBackCheckIn( + Player p, String dateStr) { + String name = p.getName(); + Object val = plugin.getDb() + .getField(name, "backcheck_dates"); + String existing = val != null + ? val.toString() : ""; + String updated = existing.isEmpty() + ? dateStr + : existing + "," + dateStr; + plugin.getDb().setField(name, + "backcheck_dates", updated); + int streak = getStreak(name); + plugin.getDb().setField(name, + "checkin_streak", streak + 1); + // ★ 补签不发放奖励,只记录 + } + + // ===== 获取N天前日期 ===== + private String getPastDate( + int daysAgo) { + Calendar cal = + Calendar.getInstance(); + cal.add(Calendar.DAY_OF_MONTH, + -daysAgo); + return new SimpleDateFormat( + "yyyy-MM-dd") + .format(cal.getTime()); + } +} \ No newline at end of file diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/CommissionManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CommissionManager.java new file mode 100644 index 0000000..cb24687 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CommissionManager.java @@ -0,0 +1,227 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.logging.Logger; + +public class CommissionManager { + + private final Main main; + private final Logger logger; + private final List rules = + new ArrayList<>(); + + public CommissionManager(Main main) { + this.main = main; + this.logger = main.getLogger(); + loadRules(); + } + + public void loadRules() { + rules.clear(); + File f = new File( + main.getDataFolder(), + "提成规则.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader br = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) + continue; + // 格式: 邀请人数:提成百分比 + // 如 "5:15" = 邀请5人提成15% + String[] parts = + line.split(":"); + if (parts.length >= 2) { + try { + int count = Integer.parseInt( + parts[0].trim()); + double rate = Double + .parseDouble( + parts[1] + .trim()) / 100.0; + rules.add(new double[]{ + count, rate}); + } catch (Exception ignored) { + } + } + } + // 按人数排序(从大到小) + rules.sort((a, b) -> + Double.compare(b[0], a[0])); + logger.info( + "[Commission] 加载 " + + rules.size() + " 条提成规则"); + } catch (Exception e) { + logger.warning( + "[Commission] 加载失败: " + + e.getMessage()); + } + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 邀请提成规则"); + pw.println("# 格式: 邀请人数:提成百分比"); + pw.println("# 邀请10人提成20%"); + pw.println("10:20"); + pw.println("# 邀请5人提成15%"); + pw.println("5:15"); + pw.println("# 邀请1人提成10%"); + pw.println("1:10"); + } catch (IOException ignored) { + } + } + + /** + * 获取邀请人的提成比例 + * 根据已邀请人数匹配最高规则 + */ + public double getCommissionRate( + String inviter) { + int inviteCount = getInviteCount( + inviter); + for (double[] rule : rules) { + if (inviteCount >= (int) rule[0]) { + return rule[1]; + } + } + return 0; + } + + /** + * 获取邀请人数 + */ + public int getInviteCount( + String inviter) { + try { + Object val = + main.getDb().getField( + inviter, + "monthly_invite_count"); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + Object total = + main.getDb().getField( + inviter, + "total_invite_count"); + if (total instanceof Number) { + return ((Number) total).intValue(); + } + } catch (Exception ignored) { + } + return 0; + } + + /** + * 发放提成 + * 被邀请人获得奖励时,邀请人获得提成 + */ + public void payCommission( + String invitee, + double originalAmount) { + // 查找邀请人 + String inviter = getInviter(invitee); + if (inviter == null + || inviter.isEmpty()) return; + + double rate = getCommissionRate( + inviter); + if (rate <= 0) return; + + double commission = + originalAmount * rate; + if (commission <= 0) return; + + // 通过 Vault 发放 + var rsp = main.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault + .economy.Economy.class); + if (rsp == null) return; + net.milkbowl.vault.economy.Economy econ = + rsp.getProvider(); + if (econ == null) return; + + econ.depositPlayer(inviter, commission); + + // 通知邀请人 + Player inviterP = + main.getServer() + .getPlayer(inviter); + if (inviterP != null + && inviterP.isOnline()) { + inviterP.sendMessage( + "§a§l[邀请提成] §f" + + invitee + " 完成任务," + + "你获得 §e" + + String.format("%.2f", + commission) + + " §f游戏币提成 (" + + (int)(rate * 100) + "%)"); + } + + logger.info( + "[Commission] 提成: " + + inviter + " ← " + + invitee + " | " + + String.format("%.2f", + originalAmount) + + " × " + + (int)(rate * 100) + "% = " + + String.format("%.2f", + commission)); + } + + /** + * 获取邀请人 + */ + private String getInviter(String player) { + try { + Object val = + main.getDb().getField( + player, + "invited_by"); + if (val != null + && !val.toString() + .isEmpty()) { + return val.toString(); + } + } catch (Exception ignored) { + } + return null; + } + + /** + * 判断金额是否包含经济内容 + */ + public boolean hasEconomyContent( + String text) { + String t = text.toLowerCase(); + return t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额"); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ConfigManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ConfigManager.java new file mode 100644 index 0000000..a15ada0 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ConfigManager.java @@ -0,0 +1,372 @@ +package Sdf1_login; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class ConfigManager { + + private final File dataFolder; + private final Map messages + = new LinkedHashMap<>(); + private final Map smtpSettings + = new LinkedHashMap<>(); + + // ===== 公共字段 ===== + public String adminTag = "admin"; + public String adminPassword = "qweasd"; + public int maxAccountsPerIP = 3; + public String approvalMode = "manual"; + public int autoApproveDelayMinutes = 30; + public int loginTimeout = 60; + public boolean afkEnabled = false; + public int afkTimeout = 300; + public boolean smtpSsl = true; + public String smtpVerifyCode = ""; + public long smtpVerifyExpire = 0; + public long smtpLastSendTime = 0; + public String smtpConfiguringPlayer = ""; + public String broadcastServerUrl = ""; + public int httpPort = 8080; + public String garbageTag = "admin"; + + public double checkinRewardFixed = 0; + public double checkinRewardMin = 0; + public double checkinRewardMax = 0; + public boolean checkinGivePoints = true; + public int checkinPoints = 10; + public String checkinRewardType = "none"; + public int backCheckPointMultiplier = 5; + public int checkinBondMin = 1; + public int checkinBondMax = 3; + + public double providerPointsPerScore = 2.0; + public int baseEconomyReward = 100; + public double providerEconomyPerScore = 50.0; + public int requesterPointsReward = 10; + + public boolean garbageEnabled = true; + public int garbageInterval = 300; + public int garbageMaxRounds = 1; + + // ★ 构造函数接收 File,和 Main.java 调用一致 ★ + public ConfigManager(File dataFolder) { + this.dataFolder = dataFolder; + } + public String rewardChannel = "债券"; // bonds = 债券,economy = Vault经济 + + + // ==================== 消息 ==================== + + public void loadMessages() { + messages.clear(); + File file = new File(dataFolder, "消息.txt"); + if (!file.exists()) createDefaultMessages(file); + loadMap(file, messages); + } + + public String msg(String key) { + String val = messages.getOrDefault(key, key); + val = val.replace("", "&l"); + val = val.replace("", "&r"); + val = val.replace("
    ", "\n"); + val = val.replace("\\n", "\n"); + val = val.replaceAll( + "&([0-9a-fk-orA-FK-OR])", "§$1"); + return val; + } + + private void createDefaultMessages(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 消息配置 ====="); + L.add("login_timeout=§c登录超时"); + L.add("not_registered=§e您尚未注册"); + L.add("not_logged_in=§e您尚未登录"); + L.add("already_registered=§c您已注册过"); + L.add("already_logged_in=§c您已登录"); + L.add("reg_success=§a注册成功!"); + L.add("login_success=§a登录成功!"); + L.add("login_failed=§c密码错误"); + L.add("password_wrong=§c密码错误"); + L.add("password_changed=§a密码修改成功!"); + L.add("password_format_error=§c密码需6位以上含大小写和数字"); + L.add("password_same=§c新密码不能与旧密码相同"); + L.add("checkin_already=§c您今日已签到"); + L.add("checkin_success=§a签到成功!获得 {points} 积分"); + L.add("chat_muted=§c你已被禁言"); + L.add("未知参数=§c未知参数或权限不足"); + writeLines(f, L); + } + + // ==================== SMTP ==================== + + public void loadSmtp() { + smtpSettings.clear(); + File file = new File(dataFolder, "SMTP设置.txt"); + if (!file.exists()) createDefaultSmtp(file); + loadMap(file, smtpSettings); + } + + public String getSmtp(String key) { + return smtpSettings.getOrDefault(key, ""); + } + + public void setSmtp(String key, String value) { + smtpSettings.put(key, value); + } + + public void saveSmtp() { + File file = new File(dataFolder, "SMTP设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + for (Map.Entry e + : smtpSettings.entrySet()) { + L.add(e.getKey() + "=" + e.getValue()); + } + writeLines(file, L); + } + + private void createDefaultSmtp(File f) { + List L = new ArrayList<>(); + L.add("# ===== SMTP 配置 ====="); + L.add("smtp地址=smtp.example.com"); + L.add("smtp端口=465"); + L.add("smtp账号="); + L.add("smtp密码="); + L.add("发件人名称=Sdf1_login"); + L.add("smtp加密=true"); + writeLines(f, L); + } + + // ==================== 插件设置 ==================== + + public void loadSettings() { + File file = new File(dataFolder, "插件设置.txt"); + if (!file.exists()) createDefaultSettings(file); + Map m = new LinkedHashMap<>(); + loadMap(file, m); + + adminTag = m.getOrDefault("管理标签", adminTag); + adminPassword = m.getOrDefault("管理密码", adminPassword); + maxAccountsPerIP = parseInt(m.getOrDefault("每IP最大账号数", "3"), 3); + approvalMode = m.getOrDefault("审批模式", approvalMode); + autoApproveDelayMinutes = parseInt(m.getOrDefault("自动审批延迟分钟", "30"), 30); + loginTimeout = parseInt(m.getOrDefault("登录超时秒数", "60"), 60); + afkEnabled = Boolean.parseBoolean(m.getOrDefault("挂机踢出", "false")); + afkTimeout = parseInt(m.getOrDefault("挂机超时秒数", "300"), 300); + smtpSsl = Boolean.parseBoolean(m.getOrDefault("smtp加密", "true")); + httpPort = parseInt(m.getOrDefault("http-port", "8080"), 8080); + broadcastServerUrl = m.getOrDefault("resource-pack-url", ""); + + providerPointsPerScore = parseDouble(m.getOrDefault("服务商积分倍率", "2.0"), 2.0); + baseEconomyReward = parseInt(m.getOrDefault("基础经济奖励", "100"), 100); + providerEconomyPerScore = parseDouble(m.getOrDefault("经济奖励每评分", "50.0"), 50.0); + requesterPointsReward = parseInt(m.getOrDefault("报单人积分奖励", "10"), 10); + + garbageEnabled = Boolean.parseBoolean(m.getOrDefault("垃圾站_启用", "true")); + garbageInterval = parseInt(m.getOrDefault("垃圾站_清理间隔秒", "300"), 300); + garbageMaxRounds = parseInt(m.getOrDefault("垃圾站_保留轮数", "1"), 1); + + checkinRewardFixed = parseDouble(m.getOrDefault("签到固定金额", "0"), 0); + checkinRewardMin = parseDouble(m.getOrDefault("签到最小金额", "0"), 0); + checkinRewardMax = parseDouble(m.getOrDefault("签到最大金额", "0"), 0); + checkinGivePoints = Boolean.parseBoolean(m.getOrDefault("签到给积分", "true")); + checkinPoints = parseInt(m.getOrDefault("签到积分数量", "10"), 10); + backCheckPointMultiplier = parseInt(m.getOrDefault("补签积分倍率", "5"), 5); + checkinBondMin = parseInt(m.getOrDefault("签到债券最小", "1"), 1); + checkinBondMax = parseInt(m.getOrDefault("签到债券最大", "3"), 3); + if (checkinBondMax < checkinBondMin) checkinBondMax = checkinBondMin; + + if (checkinRewardFixed > 0) checkinRewardType = "fixed"; + else if (checkinRewardMin > 0 && checkinRewardMax > checkinRewardMin) checkinRewardType = "range"; + else checkinRewardType = "none"; + rewardChannel = m.getOrDefault("奖励发放方式", "债券"); + rewardChannel = normalizeRewardChannel(rewardChannel); + +// 验证合法值 + if (!"economy".equalsIgnoreCase(rewardChannel) + && !"bonds".equalsIgnoreCase(rewardChannel)) { + rewardChannel = "bonds"; + } + + + + // ★ 自动补充新配置项 ★ + boolean needAppend = false; + List newKeys = new ArrayList<>(); + if (!m.containsKey("签到债券最小")) { needAppend = true; newKeys.add("签到债券最小=" + checkinBondMin); } + if (!m.containsKey("签到债券最大")) { needAppend = true; newKeys.add("签到债券最大=" + checkinBondMax); } + if (!m.containsKey("奖励发放方式")) { + needAppend = true; + newKeys.add("奖励发放方式=债券"); + } + if (needAppend) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + for (String key : newKeys) { bw.write(key); bw.newLine(); } + bw.flush(); bw.close(); + System.out.println("[Config] 已补充签到债券配置"); + + } catch (IOException ignored) {} + if (!m.containsKey("奖励发放方式")) { + try { + FileWriter fw = new FileWriter(file, true); + BufferedWriter bw = new BufferedWriter(fw); + bw.newLine(); + bw.write("奖励发放方式=bonds"); + bw.newLine(); + bw.flush(); bw.close(); + } catch (IOException ignored) {} + } + + } + } + /** + * 奖励方式标准化:中英文全兼容 + * "债券" "bonds" "Bond" "BONDS" → "bonds" + * "经济" "economy" "Economy" "ECONOMY" → "economy" + * 其他任何值 → "bonds"(默认债券) + */ + private String normalizeRewardChannel(String raw) { + if (raw == null) return "bonds"; + String s = raw.trim().toLowerCase(); + // 中文 + if (s.contains("债券")) return "bonds"; + if (s.contains("经济")) return "economy"; + // 英文 + if (s.contains("bond")) return "bonds"; + if (s.contains("econ")) return "economy"; + if (s.contains("Vault")) return "economy"; + // 兜底 + return "bonds"; + } + + public void saveSettings() { + File file = new File(dataFolder, "插件设置.txt"); + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=" + adminTag); + L.add("管理密码=" + adminPassword); + L.add("每IP最大账号数=" + maxAccountsPerIP); + L.add("审批模式=" + approvalMode); + L.add("自动审批延迟分钟=" + autoApproveDelayMinutes); + L.add("登录超时秒数=" + loginTimeout); + L.add("挂机踢出=" + afkEnabled); + L.add("挂机超时秒数=" + afkTimeout); + L.add("resource-pack-url=" + broadcastServerUrl); + L.add("http-port=" + httpPort); + L.add("垃圾站_启用=" + garbageEnabled); + L.add("垃圾站_清理间隔秒=" + garbageInterval); + L.add("垃圾站_保留轮数=" + garbageMaxRounds); + L.add("签到固定金额=" + checkinRewardFixed); + L.add("签到最小金额=" + checkinRewardMin); + L.add("签到最大金额=" + checkinRewardMax); + L.add("签到给积分=" + checkinGivePoints); + L.add("签到积分数量=" + checkinPoints); + L.add("签到债券最小=" + checkinBondMin); + L.add("签到债券最大=" + checkinBondMax); + L.add("补签积分倍率=" + backCheckPointMultiplier); + L.add("奖励发放方式=债券"); + writeLines(file, L); + } + + private void createDefaultSettings(File f) { + List L = new ArrayList<>(); + L.add("# ===== Sdf1_login 插件设置 ====="); + L.add("管理标签=admin"); + L.add("管理密码=qweasd"); + L.add("每IP最大账号数=3"); + L.add("审批模式=manual"); + L.add("自动审批延迟分钟=30"); + L.add("登录超时秒数=60"); + L.add("挂机踢出=false"); + L.add("挂机超时秒数=300"); + L.add("resource-pack-url="); + L.add("http-port=8080"); + L.add("垃圾站_启用=true"); + L.add("垃圾站_清理间隔秒=300"); + L.add("垃圾站_保留轮数=1"); + L.add("签到固定金额=0"); + L.add("签到最小金额=50"); + L.add("签到最大金额=500"); + L.add("签到给积分=true"); + L.add("签到积分数量=10"); + L.add("签到债券最小=1"); + L.add("签到债券最大=3"); + L.add("补签积分倍率=5"); + L.add("奖励发放方式=债券"); + writeLines(f, L); + } + public List getAfkWhitelist() { + List list = new ArrayList<>(); + File file = new File(dataFolder, "挂机白名单.txt"); + if (!file.exists()) { + try { + file.createNewFile(); + FileWriter fw = new FileWriter(file); + fw.write("# 每行一个玩家名\n"); + fw.flush(); + fw.close(); + } catch (IOException ignored) {} + return list; + } + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(file), + StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty() && !line.startsWith("#")) + list.add(line); + } + } catch (IOException ignored) {} + return list; + } + + // ==================== 其他 ==================== + + public String generateSmtpVerifyCode() { + String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(); + java.util.Random r = new java.util.Random(); + for (int i = 0; i < 6; i++) sb.append(chars.charAt(r.nextInt(chars.length()))); + smtpVerifyCode = sb.toString(); + smtpVerifyExpire = System.currentTimeMillis() + 300000L; + return smtpVerifyCode; + } + + public boolean checkSmtpVerifyCode(String code) { + if (System.currentTimeMillis() > smtpVerifyExpire) { smtpVerifyCode = ""; return false; } + return smtpVerifyCode.equalsIgnoreCase(code); + } + + public void clearSmtpVerifyCode() { smtpVerifyCode = ""; smtpVerifyExpire = 0; } + public boolean canSendSmtpVerify() { return System.currentTimeMillis() - smtpLastSendTime >= 60000L; } + public void recordSmtpSendTime() { smtpLastSendTime = System.currentTimeMillis(); } + + // ==================== 文件工具 ==================== + + private void loadMap(File file, Map map) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq > 0) map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); + } + } catch (IOException ignored) {} + } + + private void writeLines(File file, List lines) { + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + for (String l : lines) { w.write(l); w.newLine(); } + } catch (IOException ignored) {} + } + + private double parseDouble(String s, double def) { try { return Double.parseDouble(s.trim()); } catch (Exception e) { return def; } } + private int parseInt(String s, int def) { try { return Integer.parseInt(s.trim()); } catch (Exception e) { return def; } } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/CypayCommand.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CypayCommand.java new file mode 100644 index 0000000..7fd9067 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/CypayCommand.java @@ -0,0 +1,577 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +public class CypayCommand + implements CommandExecutor, TabCompleter { + + private final Main plugin; + private static final SimpleDateFormat SDF = + new SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss"); + + public CypayCommand(Main plugin) { + this.plugin = plugin; + } + + private boolean hasAdminPerm(CommandSender s) { + if (s.isOp()) return true; + if (!(s instanceof Player)) return true; + Player p = (Player) s; + String tag = + plugin.getConfig2().adminTag; + return !tag.isEmpty() + && p.getScoreboardTags().contains(tag); + } + + private int parseAmount(String s) { + return Integer.parseInt( + s.trim().replaceAll("[+]", "")); + } + + private List filter( + List list, String prefix) { + String low = prefix.toLowerCase(); + return list.stream() + .filter(x -> x.toLowerCase() + .startsWith(low)) + .collect(Collectors.toList()); + } + + @Override + public boolean onCommand(CommandSender sender, + Command cmd, + String label, + String[] args) { + + // ===== /cypay — 查自己 ===== + if (args.length == 0) { + String name = sender instanceof Player + ? sender.getName() : "Console"; + int bonds = + plugin.getBonds().getBonds(name); + String status = plugin.getBonds() + .getAccountStatus(name); + sender.sendMessage( + "§e§l[债券] §f" + name + + " 余额: §a" + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals(status) + ? "§c冻结中" + : "§a正常")); + return true; + } + + // ===== /cypay import ===== + /* if (args.length == 2 + && "import".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String file = args[1]; + if (!file.endsWith(".txt")) + file += ".txt"; + int result = plugin.getCDK() + .importFromFile(file); + if (result == -1) + sender.sendMessage("§c文件不存在: " + + file); + else + sender.sendMessage("§a导入完成: " + + result + " 条"); + return true; + }*/ + + // ===== /cypay freeze ===== + if (args.length == 2 + && "freeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (plugin.getBonds().isFrozen(target)) { + sender.sendMessage("§c" + target + + " 已处于冻结状态"); + return true; + } + plugin.getBonds().freezeAccount(target); + sender.sendMessage("§a已冻结 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§c§l[系统] 你的债券账户" + + "已被管理员冻结"); + return true; + } + + // ===== /cypay unfreeze ===== + if (args.length == 2 + && "unfreeze".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + if (!plugin.getBonds() + .isFrozen(target)) { + sender.sendMessage("§c" + target + + " 未被冻结"); + return true; + } + plugin.getBonds().unfreezeAccount(target); + sender.sendMessage("§a已解冻 " + target + + " 的债券账户"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a§l[系统] 你的债券账户" + + "已被解冻"); + return true; + } + + // ===== /cypay water [days] ===== + if (args.length >= 2 + && "water".equalsIgnoreCase( + args[0])) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + int days = 7; + if (args.length >= 3) { + try { + days = Integer.parseInt(args[2]); + } catch (NumberFormatException ignored) { + } + } + days = Math.min(days, + BondManager.ADMIN_MAX_WATER_DAYS); + + if (days <= 0) days = 1; + + List> txs = + plugin.getBonds() + .getTransactions( + target, days); + + sender.sendMessage( + "§e§l===== " + target + + " 流水记录 (近" + + days + "天) ====="); + sender.sendMessage("§f当前余额: §a" + + plugin.getBonds() + .getBonds(target) + + " §f状态: " + + (plugin.getBonds() + .isFrozen(target) + ? "§c冻结" : "§a正常")); + + if (txs.isEmpty()) { + sender.sendMessage("§7 (暂无记录)"); + } else { + for (Map tx : txs) { + sender.sendMessage( + formatTxLine(tx)); + } + } + sender.sendMessage( + "§e§l==========================" + + "=========="); + return true; + } + // ===== /cypay printer [player] ===== + if ("printer".equalsIgnoreCase(args[0])) { + if (!hasAdminPerm(sender) + && !(sender instanceof Player) + == false) { + // 控制台也可以用 + if (!(sender instanceof Player)) + ; // 放行 + else if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + } + if (args.length == 1) { + // 打印全服 + sender.sendMessage( + "§e正在导出全服流水..."); + plugin.getBonds(); // 确保实例就绪 + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printAll(sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + if (args.length == 2) { + // 打印指定玩家 + String target = args[1]; + sender.sendMessage("§e正在导出 " + + target + " 的流水..."); + if (plugin.getBondPrinter() != null) { + plugin.getBondPrinter() + .printPlayer(target, sender); + } else { + sender.sendMessage("§cPrinter未初始化"); + } + return true; + } + } + + // ===== /cypay give/remove ===== + if (args.length == 3) { + String action = args[0].toLowerCase(); + if ("give".equals(action) + || "remove".equals(action)) { + if (!hasAdminPerm(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + String target = args[1]; + try { + int amount = + parseAmount(args[2]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + String txType = + "give".equals(action) + ? BondManager + .TX_ADMIN_GIVE + : BondManager + .TX_ADMIN_DEDUCT; + String opName = + sender instanceof Player + ? sender.getName() + : "Console"; + + if ("give".equals(action)) { + plugin.getBonds().addBonds( + target, amount, + txType, "", + opName, "管理员发放"); + sender.sendMessage( + "§a已给 " + target + + " " + amount + + " 债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage( + "§a管理员给了你 " + + amount + + " 债券"); + } else { + if (plugin.getBonds() + .deductBonds(target, + amount, + txType, "", + opName, + "管理员扣除")) { + sender.sendMessage( + "§a已扣除 " + target + + " " + amount + + " 债券"); + Player tp = plugin + .getServer() + .getPlayerExact( + target); + if (tp != null) + tp.sendMessage( + "§c管理员扣除了" + + "你的 " + + amount + + " 债券"); + } else { + sender.sendMessage( + "§c" + target + + " 债券不足" + + "或账户冻结"); + } + } + } catch (NumberFormatException e) { + sender.sendMessage( + "§c金额格式错误"); + } + return true; + } + } + + // ===== /cypay — 仅玩家转账 ===== + if (args.length == 2) { + try { + int amount = parseAmount(args[1]); + if (amount <= 0) { + sender.sendMessage( + "§c金额必须大于0"); + return true; + } + // ★ 控制台禁止转账 + if (!(sender instanceof Player)) { + sender.sendMessage( + "§c控制台不能转账,请使用 " + + "/cypay give <玩家> <金额>"); + return true; + } + Player p = (Player) sender; + String target = args[0]; + BondManager.TransferResult r = + plugin.getBonds().transfer( + p.getName(), + target, amount); + if (r.success) { + p.sendMessage("§e§l[债券转账] §f向 §a" + + target + " §f转出 §c" + + amount + " §f枚债券"); + Player tp = plugin.getServer() + .getPlayerExact(target); + if (tp != null) + tp.sendMessage("§e§l[债券转账] §f收到 §a" + + p.getName() + + " §f转入 §a" + + amount + " §f枚债券"); + } else { + p.sendMessage("§c" + r.message + + ",当前: " + + plugin.getBonds() + .getBonds(p.getName())); + } + return true; + } catch (NumberFormatException ignored) { + } + } + + // ===== /cypay — 查指定玩家 ===== + if (args.length == 1) { + String target = args[0]; + // ★ 验证玩家在 login.db 中存在 ★ + if (!plugin.getDb().userExists(target)) { + sender.sendMessage("§c玩家 " + target + + " 不存在于服务器"); + return true; + } + + // String target = args[0]; + if (hasAdminPerm(sender)) { + int bonds = plugin.getBonds() + .getBonds(target); + String status = plugin.getBonds() + .getAccountStatus(target); + sender.sendMessage( + "§e§l[债券] §f" + target + + " 余额: §a" + + bonds); + sender.sendMessage( + "§e§l[债券] §f状态: " + + ("frozen".equals( + status) + ? "§c冻结中" + : "§a正常")); + } else { + int bonds = plugin.getBonds() + .getBonds( + sender.getName()); + sender.sendMessage( + "§e§l[债券] §f你的余额: §a" + + bonds); + } + return true; + } + + showHelp(sender); + return true; + } + + // ===== 流水行格式化 ===== + + private String formatTxLine( + Map tx) { + String type = (String) tx.get("type"); + int amount = ((Number) tx.get("amount")) + .intValue(); + String tgt = (String) tx.get("target_player"); + String reason = (String) tx.get("reason"); + int bal = ((Number) tx.get("balance_after")) + .intValue(); + long time = ((Number) tx.get("time")) + .longValue(); + + boolean pos = amount > 0; + String sign = pos ? "+" : ""; + String color = pos ? "§a" : "§c"; + + StringBuilder sb = new StringBuilder(); + sb.append("§7[") + .append(SDF.format(new Date(time))) + .append("] ") + .append(formatTxType(type)) + .append(" ") + .append(color).append(sign) + .append(amount).append("§7"); + if (tgt != null && !tgt.isEmpty()) + sb.append(" §e↔ ").append(tgt); + if (reason != null && !reason.isEmpty()) + sb.append(" §7(").append(reason) + .append(")"); + sb.append(" §7余额:§f").append(bal); + return sb.toString(); + } + + private String formatTxType(String type) { + if (type == null) return "§7未知"; + switch (type) { + case BondManager.TX_ADMIN_GIVE: + return "§a管理员发放"; + case BondManager.TX_ADMIN_DEDUCT: + return "§c管理员扣除"; + case BondManager.TX_TRANSFER_OUT: + return "§c转出"; + case BondManager.TX_TRANSFER_IN: + return "§a转入"; + case BondManager.TX_REDEEM: + return "§aCDK兑换"; + case BondManager.TX_SHOP_BUY: + return "§e商城购买"; + case BondManager.TX_DAILY_SIGN: + return "§a签到奖励"; + case BondManager.TX_FREEZE: + return "§c冻结"; + case BondManager.TX_UNFREEZE: + return "§a解冻"; + default: + return "§7" + type; + } + } + + private void showHelp(CommandSender sender) { + sender.sendMessage("§e§l债券命令帮助:"); + sender.sendMessage( + "§e/cypay §7- 查询自己余额"); + sender.sendMessage( + "§e/cypay <玩家> §7- 查余额(管理员)"); + sender.sendMessage( + "§e/cypay <玩家> <金额> §7- 转账"); + if (hasAdminPerm(sender)) { + sender.sendMessage( + "§e/cypay give <玩家> <金额> §7- 给债券"); + sender.sendMessage( + "§e/cypay remove <玩家> <金额> §7- 扣债券"); + sender.sendMessage( + "§e/cypay import <文件> §7- 导入CDK"); + sender.sendMessage( + "§e/cypay freeze <玩家> §7- 冻结"); + sender.sendMessage( + "§e/cypay unfreeze <玩家> §7- 解冻"); + sender.sendMessage( + "§e/cypay water <玩家> [天数] §7- 流水"); + sender.sendMessage( + "§e/cypay printer §7- 导出全服流水xlsx"); + sender.sendMessage( + "§e/cypay printer <玩家> §7- 导出指定玩家xlsx"); + + } + } + + @Override + public List onTabComplete( + CommandSender sender, Command command, + String label, String[] args) { + List list = new ArrayList<>(); + + if (args.length == 1) { + String last = args[0].toLowerCase(); + if (hasAdminPerm(sender)) { + list.add("give"); + list.add("remove"); + list.add("import"); + list.add("freeze"); + list.add("unfreeze"); + list.add("water"); + list.add("printer"); + + File cdkDir = new File( + plugin.getDataFolder(), + "cdk"); + if (cdkDir.exists()) { + File[] files = cdkDir.listFiles( + (d, n) -> n + .endsWith(".txt")); + if (files != null) + for (File f : files) + list.add(f.getName()); + } + } + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, last); + } + + if (args.length == 2) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first) + || "freeze".equals(first) + || "unfreeze".equals(first) + || "water".equals(first)) { + for (Player o : + Bukkit.getOnlinePlayers()) + list.add(o.getName()); + return filter(list, + args[1].toLowerCase()); + } + list.add("10"); + list.add("50"); + list.add("100"); + return filter(list, + args[1].toLowerCase()); + } + + if (args.length == 3) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first)) { + list.add("10"); + list.add("50"); + list.add("100"); + list.add("999"); + return filter(list, + args[2].toLowerCase()); + } + if ("water".equals(first)) { + list.add("1"); + list.add("3"); + list.add("7"); + list.add("14"); + return filter(list, + args[2].toLowerCase()); + } + } + + return list; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/DatabaseManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/DatabaseManager.java new file mode 100644 index 0000000..cb2b8f7 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/DatabaseManager.java @@ -0,0 +1,1758 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.logging.Logger; + +public class DatabaseManager { + + private Connection db; + private final File dataFolder; + private final Logger logger; + + public DatabaseManager(File dataFolder) { + this.dataFolder = dataFolder; + this.logger = Logger.getLogger("Sdf1_login"); + } + + // ==================== 初始化 ==================== + + public void init() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(dataFolder, + "login.db"); + db = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + + // 用户表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "users (" + + "player_name TEXT PRIMARY KEY," + + "password_hash TEXT NOT NULL," + + "password_salt TEXT NOT NULL," + + "email TEXT DEFAULT ''," + + "register_time INTEGER DEFAULT 0," + + "last_login_time INTEGER DEFAULT 0," + + "total_online_time INTEGER DEFAULT 0," + + "last_online_check INTEGER DEFAULT 0," + + "is_logged_in INTEGER DEFAULT 0," + + "blocks_broken INTEGER DEFAULT 0," + + "blocks_placed INTEGER DEFAULT 0," + + "gift_stage INTEGER DEFAULT 0," + + "gift_claimed TEXT DEFAULT ''," + + "invite_code TEXT DEFAULT ''," + + "invited_by TEXT DEFAULT ''," + + "monthly_invite_count INTEGER DEFAULT 0," + + "invite_month TEXT DEFAULT ''," + + "points INTEGER DEFAULT 0," + + "checkin_streak INTEGER DEFAULT 0," + + "last_checkin_date TEXT DEFAULT ''," + + "total_checkin_days INTEGER DEFAULT 0," + + "temp_password TEXT DEFAULT ''," + + "temp_pw_expire INTEGER DEFAULT 0," + + "temp_pw_used INTEGER DEFAULT 0," + + "ip_address TEXT DEFAULT ''," + + "tasks_completed TEXT DEFAULT ''," + + "premium_uuid TEXT DEFAULT ''," + + "register_ip TEXT DEFAULT ''," + + "register_type TEXT DEFAULT 'manual'," + + "backcheck_dates TEXT DEFAULT ''" + + ")"); + + + // 安全添加 users 缺失列 + safeAdd(st, "tasks_completed", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_password", + "TEXT DEFAULT ''"); + safeAdd(st, "temp_pw_expire", + "INTEGER DEFAULT 0"); + safeAdd(st, "temp_pw_used", + "INTEGER DEFAULT 0"); + safeAdd(st, "ip_address", + "TEXT DEFAULT ''"); + safeAdd(st, "premium_uuid", + "TEXT DEFAULT ''"); + safeAdd(st, "register_ip", + "TEXT DEFAULT ''"); + safeAdd(st, "register_type", + "TEXT DEFAULT 'manual'"); + + // ===== 背包备份表:安全迁移 ===== + // 检查旧表是否存在(用 inventory_data 列判断) + boolean hasOldTable = false; + try { + ResultSet rs = st.executeQuery( + "SELECT inventory_data " + + "FROM inventory_backups " + + "LIMIT 1"); + hasOldTable = true; + rs.close(); + } catch (SQLException e) { + hasOldTable = false; + } + + if (hasOldTable) { + // 旧表存在,迁移数据到新表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups_new (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + // 迁移旧数据 + try { + st.execute("INSERT INTO " + + "inventory_backups_new " + + "(player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience) " + + "SELECT player_name, " + + "inventory_data, " + + "armor_data, " + + "extra_data, " + + "player_level, " + + "player_exp " + + "FROM inventory_backups"); + } catch (SQLException ignored) { + } + st.execute("DROP TABLE " + + "inventory_backups"); + st.execute("ALTER TABLE " + + "inventory_backups_new " + + "RENAME TO " + + "inventory_backups"); + } else { + // 无旧表或已迁移,直接创建 + st.execute("CREATE TABLE IF NOT EXISTS " + + "inventory_backups (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "contents_b64 TEXT DEFAULT ''," + + "armor_b64 TEXT DEFAULT ''," + + "extra_b64 TEXT DEFAULT ''," + + "level INTEGER DEFAULT 0," + + "experience REAL DEFAULT 0," + + "save_time INTEGER DEFAULT 0" + + ")"); + } + + // 安全报警表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "security_alerts (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "alert_ip TEXT NOT NULL," + + "alert_time INTEGER NOT NULL," + + "alert_date TEXT NOT NULL," + + "notified INTEGER DEFAULT 0," + + "notified_time INTEGER DEFAULT 0)"); + // PVP区域表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "name TEXT NOT NULL UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER DEFAULT 0" + + ")"); + +// PVP击杀总榜表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT NOT NULL," + + "region_name TEXT NOT NULL," + + "kills INTEGER DEFAULT 0," + + "deaths INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, region_name)" + + ")"); + + +// 菜单图标表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "menu_icons (" + + "player_name TEXT PRIMARY KEY," + + "icon_b64 TEXT DEFAULT ''," + + "item_name TEXT DEFAULT ''," + + "save_time INTEGER DEFAULT 0" + + ")"); + + // 工单表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "tickets (" + + "id INTEGER PRIMARY KEY AUTOINCREMENT," + + "type TEXT NOT NULL," + + "status TEXT DEFAULT 'pending'," + + "priority TEXT DEFAULT 'normal'," + + "requester TEXT NOT NULL," + + "assigned_to TEXT DEFAULT ''," + + "title TEXT DEFAULT ''," + + "description TEXT DEFAULT ''," + + "reward_amount INTEGER DEFAULT 0," + + "score INTEGER DEFAULT 0," + + "target_player TEXT DEFAULT ''," + + "create_time INTEGER NOT NULL," + + "update_time INTEGER DEFAULT 0," + + "complete_time INTEGER DEFAULT 0," + + "admin_confirmed INTEGER DEFAULT 0," + + "reject_reason TEXT DEFAULT ''" + + ")"); + + // 工单回复表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "ticket_replies (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "ticket_id INTEGER NOT NULL," + + "sender TEXT NOT NULL," + + "message TEXT NOT NULL," + + "create_time INTEGER NOT NULL" + + ")"); + + // 服务商表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "service_providers (" + + "player_name TEXT PRIMARY KEY," + + "role TEXT DEFAULT 'waiter'," + + "active INTEGER DEFAULT 1," + + "join_time INTEGER DEFAULT 0" + + ")"); + // 垃圾回收站物品表 + st.execute("CREATE TABLE IF NOT EXISTS " + + "items (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "player_name TEXT NOT NULL," + + "item_data TEXT DEFAULT ''," + + "amount INTEGER DEFAULT 1," + + "create_time INTEGER DEFAULT 0" + + ")"); + safeAdd(st, "menu_snowball", + "INTEGER DEFAULT 1"); + st.close(); + logger.info("[Sdf1_login] 数据库初始化完成"); + } catch (Exception e) { + throw new RuntimeException( + "[Sdf1_login] DB初始化失败: " + + e.getMessage(), e); + } + } + + + private void safeAdd(Statement st, + String col, String def) { + try { + st.executeUpdate("ALTER TABLE users " + + "ADD COLUMN " + col + " " + def); + } catch (SQLException ignored) { + } + } + + public void close() { + try { + if (db != null && !db.isClosed()) + db.close(); + } catch (Exception ignored) { + } + } +// ==================== 菜单图标 ==================== + + public void saveMenuIcon(String name, + String iconB64, String itemName) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO menu_icons " + + "(player_name, icon_b64, " + + "item_name, save_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, iconB64); + ps.setString(3, itemName); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT icon_b64 FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + String b64 = null; + if (rs.next()) { + b64 = rs.getString("icon_b64"); + } + rs.close(); + ps.close(); + return b64; + } catch (SQLException e) { + return null; + } + } + + public void deleteMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM menu_icons " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasMenuIcon(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM menu_icons " + + "WHERE player_name=? " + + "AND icon_b64 IS NOT NULL " + + "AND icon_b64 != '' " + + "LIMIT 1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + // ==================== 用户操作 ==================== + + public boolean userExists(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public void createUser(String name, String hash, + String salt) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO users " + + "(player_name, " + + "password_hash, " + + "password_salt, " + + "register_time) " + + "VALUES (?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, hash); + ps.setString(3, salt); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ========== 临时密码管理 ========== + + public void clearTempPassword(String name) { + setField(name, "temp_password", null); + setField(name, "temp_pw_expire", 0); + setField(name, "temp_pw_used", 1); + } + + public void clearAllTempPasswords() { + try { + Statement st = db.createStatement(); + st.executeUpdate( + "UPDATE users SET " + + "temp_password=NULL," + + "temp_pw_expire=0," + + "temp_pw_used=1 " + + "WHERE temp_password " + + "IS NOT NULL"); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPlayerIPByName( + String name) { + Object ip = getField(name, "ip"); + return ip != null + ? String.valueOf(ip) : null; + } + + public boolean checkPasswordOrTemp( + String name, String hash) { + // 先检查主密码 + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String mainHash = + rs.getString("password_hash"); + rs.close(); + ps.close(); + if (hash.equals(mainHash)) + return true; + } else { + rs.close(); + ps.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + // 再检查临时密码 + try { + PreparedStatement ps2 = db.prepareStatement( + "SELECT temp_password, " + + "temp_pw_expire, " + + "temp_pw_used " + + "FROM users " + + "WHERE player_name=?"); + ps2.setString(1, name); + ResultSet rs2 = ps2.executeQuery(); + if (rs2.next()) { + String tempHash = + rs2.getString( + "temp_password"); + long expire = + rs2.getLong( + "temp_pw_expire"); + int used = + rs2.getInt( + "temp_pw_used"); + rs2.close(); + ps2.close(); + if (tempHash != null + && !tempHash.isEmpty() + && hash.equals(tempHash) + && System.currentTimeMillis() + < expire + && used != 1) { + return true; + } + } else { + rs2.close(); + ps2.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + public Map getUser(String name) { + Map r = + new LinkedHashMap<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + r.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return r; + } + + public void setField(String name, String field, + Object val) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE users SET " + field + + "=? " + + "WHERE player_name=?"); + if (val instanceof Integer) + ps.setInt(1, (Integer) val); + else if (val instanceof Long) + ps.setLong(1, (Long) val); + else + ps.setString(1, + String.valueOf(val)); + ps.setString(2, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Object getField(String name, + String field) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT " + field + + " FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + Object val = rs.next() + ? rs.getObject(1) : null; + rs.close(); + ps.close(); + return val; + } catch (SQLException e) { + return null; + } + } + + public boolean checkPassword(String name, + String hash) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT password_hash " + + "FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String stored = + rs.getString("password_hash"); + rs.close(); + ps.close(); + return hash.equals(stored); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + + public boolean isUsingTempPassword(String name) { + String h = (String) getField(name, + "temp_password"); + return h != null && !h.isEmpty(); + } + + public void setLoggedIn(String name, + boolean val) { + setField(name, "is_logged_in", + val ? 1 : 0); + } + + public void addPoints(String name, int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + setField(name, "points", curVal + amount); + } + + public boolean deductPoints(String name, + int amount) { + Object cur = getField(name, "points"); + int curVal = cur instanceof Number + ? ((Number) cur).intValue() : 0; + if (curVal < amount) return false; + setField(name, "points", + curVal - amount); + return true; + } + public List> + getAllUsers() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM users"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + public List> + getTicketsByProviderAll( + String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deleteUser(String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM users " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void recordIP(String name, String ip) { + if (ip != null && !ip.isEmpty()) { + setField(name, "ip_address", ip); + } + } + + // ==================== IP查询 ==================== + + /** + * 统计指定IP注册的账号数量 + */ + public int countIPAccounts(String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address=?"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count; + } catch (SQLException e) { + return 0; + } + } + + /** + * 获取指定IP下所有账号详情 + */ + public List> + getAccountDetailsByIP(String ip) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, " + + "register_time " + + "FROM users " + + "WHERE ip_address=? " + + "ORDER BY register_time"); + ps.setString(1, ip); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("register_time", + rs.getLong("register_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 背包备份 ==================== + + public void saveInventoryBackup(String name, + String contents, String armor, + String extra, int level, + double experience) { + try { + PreparedStatement del = + db.prepareStatement( + "DELETE FROM " + + "inventory_backups " + + "WHERE player_name=?"); + del.setString(1, name); + del.executeUpdate(); + del.close(); + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO " + + "inventory_backups" + + " (player_name, " + + "contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience, " + + "save_time) " + + "VALUES " + + "(?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, contents); + ps.setString(3, armor); + ps.setString(4, extra); + ps.setInt(5, level); + ps.setDouble(6, experience); + ps.setLong(7, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public String[] loadInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time " + + "DESC LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void deleteInventoryBackup( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM inventory_backups " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean hasInventoryBackup( + String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id FROM " + + "inventory_backups " + + "WHERE player_name=? " + + "LIMIT 1"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + boolean has = rs.next(); + rs.close(); + ps.close(); + return has; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } +// ==================== PVP区域 ==================== + + public void savePVPRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions " + + "(name, world, " + + "x1, y1, z1, " + + "x2, y2, z2, " + + "create_time) " + + "VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map + getPVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getAllPVPRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + db.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void deletePVPRegion(String name) { + try { + PreparedStatement ps = + db.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +// ==================== PVP击杀 ==================== + + public int getPVPKills( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int kills = 0; + if (rs.next()) + kills = rs.getInt("kills"); + rs.close(); + ps.close(); + return kills; + } catch (SQLException e) { + return 0; + } + } + + public int getPVPDeaths( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=? " + + "AND region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = ps.executeQuery(); + int deaths = 0; + if (rs.next()) + deaths = rs.getInt("deaths"); + rs.close(); + ps.close(); + return deaths; + } catch (SQLException e) { + return 0; + } + } + + public void addPVPKill( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,1,0) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "kills=kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void addPVPDeath( + String player, String region) { + try { + PreparedStatement ps = + db.prepareStatement( + "INSERT INTO pvp_kills " + + "(player_name, " + + "region_name, " + + "kills, deaths) " + + "VALUES (?,?,0,1) " + + "ON CONFLICT" + + "(player_name, " + + "region_name) " + + "DO UPDATE SET " + + "deaths=deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getPVPKillTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT player_name, " + + "kills, deaths " + + "FROM pvp_kills " + + "WHERE " + + "region_name=? " + + "AND kills>0 " + + "ORDER BY kills " + + "DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString("player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + // ==================== 安全报警 ==================== + + public void recordSecurityAlert( + String playerName, String ip) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO security_alerts " + + "(player_name, alert_ip, " + + "alert_time, alert_date) " + + "VALUES (?,?,?,?)"); + ps.setString(1, playerName); + ps.setString(2, ip); + long now = System.currentTimeMillis(); + ps.setLong(3, now); + ps.setString(4, + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date( + now))); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + + } + + public List> + getUnnotifiedAlerts( + String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM security_alerts " + + "WHERE player_name=? " + + "AND notified=0 " + + "ORDER BY alert_time ASC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void markAlertNotified(int alertId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE security_alerts " + + "SET notified=1, " + + "notified_time=? " + + "WHERE id=?"); + ps.setLong(1, + System.currentTimeMillis()); + ps.setInt(2, alertId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + /** + * 获取玩家最近N条背包备份 + */ + public List> + getInventoryBackups(String name, + int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT id, save_time " + + "FROM inventory_backups " + + "WHERE player_name=? " + + "ORDER BY save_time DESC " + + "LIMIT ?"); + ps.setString(1, name); + ps.setInt(2, limit); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("id", + rs.getInt("id")); + row.put("save_time", + rs.getLong("save_time")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + + /** + * 获取指定ID的备份数据 + */ + public String[] getInventoryBackupById( + int backupId) { + try { + PreparedStatement ps = + db.prepareStatement( + "SELECT contents_b64, " + + "armor_b64, " + + "extra_b64, " + + "level, " + + "experience " + + "FROM inventory_backups " + + "WHERE id=?"); + ps.setInt(1, backupId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String[] result = + new String[5]; + result[0] = rs.getString( + "contents_b64"); + result[1] = rs.getString( + "armor_b64"); + result[2] = rs.getString( + "extra_b64"); + result[3] = String.valueOf( + rs.getInt("level")); + result[4] = String.valueOf( + rs.getDouble("experience")); + rs.close(); + ps.close(); + return result; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + + // ==================== 工单操作 ==================== + + public int createTicket(String type, + String status, String priority, + String requester, String title, + String description, + int rewardAmount, + String targetPlayer) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO tickets " + + "(type, status, priority, " + + "requester, title, " + + "description, " + + "reward_amount, " + + "target_player, " + + "create_time) " + + "VALUES (?,?,?,?,?,?,?,?,?)"); + ps.setString(1, type); + ps.setString(2, status); + ps.setString(3, priority); + ps.setString(4, requester); + ps.setString(5, title); + ps.setString(6, description); + ps.setInt(7, rewardAmount); + ps.setString(8, targetPlayer); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ResultSet rs = + ps.getGeneratedKeys(); + int id = -1; + if (rs.next()) id = rs.getInt(1); + rs.close(); + ps.close(); + return id; + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + public void updateTicketStatus(int ticketId, + String status) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET status=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, status); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void assignTicket(int ticketId, + String provider) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "assigned_to=?, " + + "status='processing', " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, provider); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void completeTicket(int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='completed', " + + "complete_time=?, " + + "update_time=? " + + "WHERE id=?"); + long now = System.currentTimeMillis(); + ps.setLong(1, now); + ps.setLong(2, now); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void confirmTicket(int ticketId, + int score) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "admin_confirmed=1, " + + "score=?, " + + "status='resolved', " + + "update_time=? " + + "WHERE id=?"); + ps.setInt(1, score); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + + public void rejectTicketComplete(int ticketId, + String reason) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + + "status='rejected', " + + "reject_reason=?, " + + "update_time=? " + + "WHERE id=?"); + ps.setString(1, reason); + ps.setLong(2, + System.currentTimeMillis()); + ps.setInt(3, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 更新工单任意字段 + */ + public void updateTicketField(int ticketId, + String field, Object value) { + try { + PreparedStatement ps = db.prepareStatement( + "UPDATE tickets SET " + field + + "=? WHERE id=?"); + if (value instanceof Integer) + ps.setInt(1, (Integer) value); + else if (value instanceof Long) + ps.setLong(1, (Long) value); + else + ps.setString(1, + String.valueOf(value)); + ps.setInt(2, ticketId); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public Map getTicket( + int ticketId) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE id=?"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public List> + getTicketsByStatus(String status) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status=? " + + "ORDER BY create_time DESC"); + ps.setString(1, status); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public void addTicketReply(int ticketId, + String sender, String message) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT INTO ticket_replies " + + "(ticket_id, sender, " + + "message, create_time) " + + "VALUES (?,?,?,?)"); + ps.setInt(1, ticketId); + ps.setString(2, sender); + ps.setString(3, message); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + // 更新工单状态为已回复 + updateTicketStatus(ticketId, + "replied"); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public List> + getTicketReplies(int ticketId) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM ticket_replies " + + "WHERE ticket_id=? " + + "ORDER BY create_time ASC"); + ps.setInt(1, ticketId); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + /** + * 获取已回复但超时未互动的工单 + */ + public List> + getRepliedStaleTickets( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='replied' " + + "AND update_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByPlayer(String playerName) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE requester=? " + + "ORDER BY create_time DESC"); + ps.setString(1, playerName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getTicketsByProvider(String provider) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE assigned_to=? " + + "AND (status='processing' " + + "OR status='replied') " + + "ORDER BY create_time DESC"); + ps.setString(1, provider); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public List> + getUnconfirmedCompleted( + long timeoutMs) { + List> list = + new ArrayList<>(); + try { + long cutoff = + System.currentTimeMillis() + - timeoutMs; + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM tickets " + + "WHERE status='completed' " + + "AND admin_confirmed=0 " + + "AND complete_time < ?"); + ps.setLong(1, cutoff); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + // ==================== 服务商操作 ==================== + + public void addServiceProvider(String name, + String role) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO " + + "service_providers " + + "(player_name, role, " + + "active, join_time) " + + "VALUES (?,?,1,?)"); + ps.setString(1, name); + ps.setString(2, role); + ps.setLong(3, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void removeServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM service_providers " + + "WHERE player_name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean isServiceProvider( + String name) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM service_providers " + + "WHERE player_name=? " + + "AND active=1"); + ps.setString(1, name); + boolean r = ps.executeQuery().next(); + ps.close(); + return r; + } catch (SQLException e) { + return false; + } + } + + public List> + getAllServiceProviders() { + List> list = + new ArrayList<>(); + try { + Statement st = db.createStatement(); + ResultSet rs = st.executeQuery( + "SELECT * FROM service_providers " + + "WHERE active=1"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs.getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Connection getDb() { + return db; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/EmailManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/EmailManager.java new file mode 100644 index 0000000..b6906e5 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/EmailManager.java @@ -0,0 +1,284 @@ +package Sdf1_login; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Base64; +import java.util.logging.Logger; + +public class EmailManager { + + private final ConfigManager config; + private final Logger logger; + + public EmailManager(ConfigManager config) { + this.config = config; + this.logger = Logger.getLogger("Sdf1_login"); + } + + public boolean isConfigured() { + String host = config.getSmtp("smtp地址"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + return host != null && !host.isEmpty() + && user != null && !user.isEmpty() + && pass != null && !pass.isEmpty(); + } + + public boolean sendVerifyCode(String to, + String code) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置," + + "无法发送验证码"); + return false; + } + logger.info("[Sdf1_login] 发送验证码到 " + to); + return sendEmailSync(to, + "[Sdf1_login] 验证码", + "您的验证码: " + code + + "\n5分钟内有效。\n" + + "如非本人操作请忽略。"); + } + + public boolean sendTempPassword(String to, + String player, String tempPwd) { + if (!isConfigured()) { + logger.warning( + "[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, + "[Sdf1_login] 临时密码", + "玩家 " + player + + " 的临时密码: " + tempPwd + + "\n请尽快登录并修改密码!"); + } + + public boolean sendBody(String to, String subject, String body) { + if (!isConfigured()) { + logger.warning("[Sdf1_login] SMTP未配置"); + return false; + } + return sendEmailSync(to, subject, body); + } + + + private boolean sendEmailSync(String to, + String subject, String body) { + final boolean[] result = {false}; + Thread t = new Thread(() -> { + try { + result[0] = sendEmail( + to, subject, body); + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件线程异常: " + + e.getMessage()); + } + }, "Sdf1_login-SMTP"); + t.setDaemon(true); + t.start(); + try { + t.join(15000); + } catch (InterruptedException ignored) { + } + if (t.isAlive()) { + t.interrupt(); + logger.severe( + "[Sdf1_login] SMTP超时(15秒)"); + return false; + } + return result[0]; + } + + private boolean sendEmail(String to, + String subject, String body) { + String host = config.getSmtp("smtp地址"); + String portStr = config.getSmtp("smtp端口"); + String user = config.getSmtp("smtp账号"); + String pass = config.getSmtp("smtp密码"); + String fromName = config.getSmtp( + "发件人名称"); + + int port; + try { + port = Integer.parseInt(portStr); + } catch (Exception e) { + port = 465; + } + + try { + String encSubject = "=?UTF-8?B?" + + Base64.getEncoder() + .encodeToString( + subject.getBytes("UTF-8")) + + "?="; + String encBody = Base64.getEncoder() + .encodeToString( + body.getBytes("UTF-8")); + + if (port == 465) { + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket socket = (SSLSocket) + factory.createSocket( + host, port); + socket.setSoTimeout(10000); + doSmtp(socket, host, user, pass, + to, encSubject, encBody, + fromName); + socket.close(); + } else { + Socket plainSocket = + new Socket(host, port); + plainSocket.setSoTimeout(10000); + BufferedReader preReader = + new BufferedReader( + new InputStreamReader( + plainSocket + .getInputStream(), + "UTF-8")); + PrintWriter preWriter = + new PrintWriter( + plainSocket + .getOutputStream(), + true); + checkResponse(preReader, "220"); + sendLine(preWriter, + "EHLO " + host); + checkResponse(preReader, "250"); + sendLine(preWriter, "STARTTLS"); + checkResponse(preReader, "220"); + SSLSocketFactory factory = + (SSLSocketFactory) + SSLSocketFactory + .getDefault(); + SSLSocket sslSocket = (SSLSocket) + factory.createSocket( + plainSocket, host, + port, true); + sslSocket.startHandshake(); + doSmtp(sslSocket, host, user, pass, + to, encSubject, encBody, + fromName); + sslSocket.close(); + } + logger.info( + "[Sdf1_login] 邮件发送成功 -> " + + to); + return true; + } catch (Exception e) { + logger.severe( + "[Sdf1_login] 邮件异常: " + + e.getClass() + .getSimpleName() + + ": " + e.getMessage()); + if (e.getCause() != null) { + logger.severe( + "[Sdf1_login] 根因: " + + e.getCause() + .getMessage()); + } + return false; + } + } + + private void doSmtp(SSLSocket socket, + String host, String user, String pass, + String to, String subject, String body, + String fromName) throws Exception { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + socket.getInputStream(), + "UTF-8")); + OutputStream rawOut = + socket.getOutputStream(); + PrintWriter writer = + new PrintWriter( + new java.io.BufferedOutputStream( + rawOut), true); + + checkResponse(reader, "220"); + sendLine(writer, "EHLO " + host); + checkResponse(reader, "250"); + sendLine(writer, "AUTH LOGIN"); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + user.getBytes("UTF-8"))); + checkResponse(reader, "334"); + sendLine(writer, Base64.getEncoder() + .encodeToString( + pass.getBytes("UTF-8"))); + checkResponse(reader, "235"); + sendLine(writer, + "MAIL FROM:<" + user + ">"); + checkResponse(reader, "250"); + sendLine(writer, + "RCPT TO:<" + to + ">"); + checkResponse(reader, "250"); + sendLine(writer, "DATA"); + checkResponse(reader, "354"); + sendLine(writer, + "From: " + fromName + + " <" + user + ">"); + sendLine(writer, + "To: <" + to + ">"); + sendLine(writer, + "Reply-To: <" + user + ">"); + sendLine(writer, + "Subject: " + subject); + sendLine(writer, "MIME-Version: 1.0"); + sendLine(writer, + "Content-Type: text/plain; " + + "charset=UTF-8"); + sendLine(writer, + "Content-Transfer-Encoding: " + + "base64"); + sendLine(writer, ""); + sendLine(writer, body); + sendLine(writer, "."); + checkResponse(reader, "250"); + sendLine(writer, "QUIT"); + } + + private void sendLine(PrintWriter writer, + String line) { + writer.print(line + "\r\n"); + writer.flush(); + } + + private void checkResponse( + BufferedReader reader, + String expected) throws Exception { + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line).append("\n"); + if (line.length() >= 4 + && Character.isDigit( + line.charAt(0)) + && line.charAt(3) == ' ') + break; + } + String resp = sb.toString().trim(); + if (!resp.startsWith(expected)) { + if (resp.startsWith("535")) + logger.severe( + "[Sdf1_login] SMTP 535 " + + "认证失败"); + throw new Exception( + "SMTP响应异常: 期望 " + + expected + + ",实际: " + resp); + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/GUIManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/GUIManager.java new file mode 100644 index 0000000..cbc7102 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/GUIManager.java @@ -0,0 +1,2681 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + + + + +import java.util.*; + +public class GUIManager implements Listener { + + private final Main plugin; + + public static final String T_MAIN = + "§6§l服务器菜单"; + public static final String T_MORE = + "§6§l更多功能"; + public static final String T_EDITOR = + "§6§l编辑菜单项"; + public static final String T_BIOME = + "§b§l目标群系传送"; + public static final String T_MY_INFO = + "§6§l我的信息"; + public static final String T_GIFT_STAGES = + "§6§l礼包阶段"; + public static final String T_ADMIN = + "§6§l管理员面板"; + public static final String T_INVITE = + "§6§l邀请数据"; + public static final String T_USER_MGMT = + "§6§l用户管理"; + public static final String T_TASK_CENTER = + "§6§l任务中心"; + private final Map userMgmtPages = new HashMap<>(); + private static final int UMGMT_PAGE_SIZE = 45; + + + + public GUIManager(Main plugin) { + this.plugin = plugin; + } + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_MAIN); +// ===== 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + + // 已占用的槽位 + Set occupied = new HashSet<>(); + occupied.add(10); + occupied.add(12); + occupied.add(14); + occupied.add(16); + occupied.add(19); + occupied.add(21); + occupied.add(23); + occupied.add(25); + occupied.add(29); + occupied.add(31); + occupied.add(32); + occupied.add(34); + + // 自定义区域:第1排(0-8) + 第5排(36-44) + 第6排(45-53) + int[] customArea = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, + 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53 + }; + + int autoIdx = 0; + for (int ci = 0; + ci < customs.size() && ci < 27; ci++) { + MenuManager.MenuItem mi = + customs.get(ci); + + // 权限过滤 + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + + int targetSlot = -1; + + // 玩家指定了格子? + if (mi.slot != null) { + targetSlot = findBestSlot( + mi.slot - 1, occupied); + } + + // 未指定或冲突 → 自动排队 + if (targetSlot < 0) { + while (autoIdx < customArea.length) { + int s = customArea[autoIdx++]; + if (!occupied.contains(s)) { + targetSlot = s; + break; + } + } + } + + if (targetSlot < 0 + || targetSlot >= 54) continue; + occupied.add(targetSlot); + + // 放置物品 + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(java.util.Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(targetSlot, icon); + } + } + +// 背景填充 + fillBg(g); + p.openInventory(g); + + // ===== Row1(9-17): 核心功能 ===== + g.setItem(10, mkItem(Material.PLAYER_HEAD, + "§a§l我的信息")); + g.setItem(12, mkItem(Material.PAPER, + "§b§l邀请数据")); + g.setItem(14, mkItem(Material.EMERALD, + "§d§l积分商城")); + g.setItem(16, mkItem(Material.COMPASS, + "§b§l群系传送", + "§7传送到指定群系")); + + // ===== Row2(18-26): 日常功能 ===== + g.setItem(19, mkItem(Material.BOOK, + "§6工单系统")); + g.setItem(21, mkItem(Material.EMERALD_BLOCK, + "§a§l每日签到", + "§7签到获取积分和惊喜奖励")); + g.setItem(23, mkItem(Material.ENDER_CHEST, + "§6§l任务中心")); + if (p.isOp() || isAdmin(p)) { + g.setItem(25, mkItem(Material.HOPPER, + "§6§l垃圾回收站", + "§7查看和取回清理的物品")); + + + } + // ===== Row3(27-35): 管理功能 ===== + // ★ 商店 - 所有人都能看 ★ + g.setItem(27, mkItem(Material.CHEST, + "§6§l商店", + "§7浏览、购买、出售物品", + "§7货币: 债券")); + + if (isAdmin(p)) { + g.setItem(29, mkItem( + Material.REDSTONE_BLOCK, + "§c§l管理员面板")); + } + if (p.isOp()) { + g.setItem(31, mkItem(Material.ECHO_SHARD, + "§c§l管理菜单", + "§7添加、编辑、删除菜单项")); + } + // 在 openMain 方法中,Row3 区域加: + + // ★ 余额查询 - 所有人都能看 ★ + double myBal = 0; + try { + var reg = plugin.getServer() + .getServicesManager() + .getRegistration( + net.milkbowl.vault.economy + .Economy.class); + if (reg != null) + myBal = reg.getProvider() + .getBalance(p); + } catch (Exception ignored) {} + int myBonds = plugin.getBonds().getBonds(p.getName()); + g.setItem(32, mkItem(Material.EMERALD, + "§b§l我的钱包", + "§7查看债券余额与流水记录")); + +// ★ 余额操作 - 仅OP和tag玩家 ★ + if (isAdmin(p)) { + g.setItem(34, mkItem(Material.GOLD_INGOT, + "§6§l余额操作", + "§7给钱、扣钱")); + } + + + + // ===== Row0(0-8): 自定义菜单项 ===== + if (plugin.getMenu() != null) { + List customs = + plugin.getMenu().getItems(); + for (int i = 0; + i < customs.size() && i < 9; + i++) { + MenuManager.MenuItem mi = + customs.get(i); + if ("OP".equals(mi.permType) + && !p.isOp() + && !isAdmin(p)) continue; + ItemStack icon = + mi.icon.clone(); + ItemMeta im = + icon.getItemMeta(); + im.setDisplayName( + "§e§l" + mi.title); + im.setLore(Arrays.asList( + "§7指令: §f" + + mi.command, + "§7权限: §f" + + mi.permType, + "", + "§a左键执行")); + icon.setItemMeta(im); + g.setItem(i, icon); + } + } + + // 背景最后填充(不覆盖物品) + fillBg(g); + p.openInventory(g); + } + private int findBestSlot( + int requested, + Set occupied) { + // 目标位空闲直接用 + if (requested >= 0 && requested < 54 + && !occupied.contains(requested)) { + return requested; + } + // BFS上下左右找最近空位 + int row = requested / 9; + int col = requested % 9; + boolean[][] visited = + new boolean[6][9]; + java.util.Queue queue = + new java.util.LinkedList<>(); + queue.add(new int[]{row, col}); + visited[row][col] = true; + int[][] dirs = { + {-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + while (!queue.isEmpty()) { + int[] cur = queue.poll(); + for (int[] d : dirs) { + int nr = cur[0] + d[0]; + int nc = cur[1] + d[1]; + if (nr >= 0 && nr < 6 + && nc >= 0 && nc < 9 + && !visited[nr][nc]) { + visited[nr][nc] = true; + int s = nr * 9 + nc; + if (!occupied.contains(s)) { + return s; + } + queue.add(new int[]{nr, nc}); + } + } + } + return -1; + } + + public void openMenuManager(Player p) { + List list = + plugin.getMenu().getItems(); + Inventory g = Bukkit.createInventory( + null, 27, + "§c§l菜单管理"); + fillBg(g); + + for (int i = 0; + i < list.size() && i < 9; i++) { + MenuManager.MenuItem mi = list.get(i); + g.setItem(i, mkItem( + mi.icon.getType(), + "§e" + mi.title, + "§7指令: " + mi.command, + "§7权限: " + mi.permType, + "", + "§a左键编辑", + "§7右键切换权限")); + } + + g.setItem(22, mkItem(Material.LIME_WOOL, + "§a§l添加菜单项")); + + g.setItem(26, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + + public void openBalanceOps(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§d§l余额操作"); + fillBg(g); + + net.milkbowl.vault.economy.Economy eco = null; + try { + var reg = plugin.getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); + if (reg != null) eco = reg.getProvider(); + } catch (Exception ignored) {} + + int slot = 0; + for (Player op : Bukkit.getOnlinePlayers()) { + if (slot >= 45) break; + double bal = 0; + if (eco != null) bal = eco.getBalance(op); + boolean isSelf = op.getName().equals(p.getName()); + int bondBal = plugin.getBonds().getBonds(op.getName()); + g.setItem(slot, mkItem( + isSelf ? Material.EMERALD_BLOCK : Material.PLAYER_HEAD, + "§e" + op.getName(), + "§7金币: §a$" + String.format("%.2f", bal), + "§7债券: §e" + bondBal + " §6枚", + "", + "§a左键经济(±) §6右键债券(±)")); + slot++; + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + p.openInventory(g); + } + + public void openShop(Player p) { + p.closeInventory(); + plugin.getShopManager().openShopMain(p); + } + + public void openTicket(Player p) { + p.closeInventory(); + p.performCommand("sdf1_login ticket"); + } + @EventHandler + public void onInvClose( + + InventoryCloseEvent e) { + if (!(e.getPlayer() instanceof Player)) + return; + if (!(e.getPlayer() instanceof Player)) return; + Player p = (Player) e.getPlayer(); + Player p3 = (Player) e.getPlayer(); + String t = e.getView().getTitle(); + if (!(e.getPlayer() instanceof Player)) return; + Player p2 = (Player) e.getPlayer(); + + if (!t.equals(T_EDITOR)) return; + + // ★ 编辑器关闭时清理空项 ★ + List list = + plugin.getMenu().getItems(); + if (!list.isEmpty()) { + MenuManager.MenuItem last = + list.get(list.size() - 1); + if ((last.command == null + || last.command.trim().isEmpty() + || last.command.trim() + .equals("/")) + && "新菜单项" + .equals(last.title)) { + list.remove(list.size() - 1); + } + } + } + + public void openSubMenu( + Player p, String fileName) { + String menuTitle = + "§6§l" + fileName + .replace(".txt", ""); + Inventory g = Bukkit.createInventory( + null, 54, menuTitle); + fillBg(g); + + java.io.File f = new java.io.File( + plugin.getDataFolder(), fileName); + if (!f.exists()) { + p.sendMessage("§c文件不存在: " + + fileName); + return; + } + try { + List lines = + java.nio.file.Files + .readAllLines( + f.toPath(), + java.nio.charset + .StandardCharsets + .UTF_8); + List items = + new ArrayList<>(); + StringBuilder buf = + new StringBuilder(); + boolean inItem = false; + + for (String raw : lines) { + String l = raw.trim(); + if (l.startsWith("#") + || l.startsWith("//")) + continue; + if (l.contains("")) { + inHtml = false; + l = l.substring( + l.indexOf("-->") + 3) + .trim(); + } else { + plugin.getLogger().info( + "[Menu] L" + lineNum + + ": [HTML注释中]"); + continue; + } + } + + // ===== 第3优先:整行注释(最高裁决) ===== + // 以 # 或 // 开头的行,直接跳过 + // 不管后面包含什么 /* "); + pw.println(); + pw.println("{"); + pw.println(" 标题:主城传送"); + pw.println(" 指令:/spawn"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:COMPASS"); + pw.println("}"); + pw.println(); + pw.println("{"); + pw.println(" 标题:随机传送"); + pw.println(" 指令:/rtp"); + pw.println(" 权限类型:玩家"); + pw.println(" 图标:ENDER_PEARL"); + pw.println("}"); + } catch (Exception ignored) { + } + } + + // ===== 保存 ===== + public void saveMenu() { + File f = new File( + plugin.getDataFolder(), + "菜单.txt"); + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8))) { + pw.println("# 服务器菜单"); + pw.println(); + for (MenuItem mi : items) { + pw.println("{"); + pw.println(" 标题:" + + mi.title); + pw.println(" 指令:" + + mi.command); + pw.println(" 权限类型:" + + mi.permType); + // ★ 保存实际材质名 ★ + pw.println(" 图标:" + + mi.icon.getType().name()); + pw.println("}"); + pw.println(); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Menu] 保存失败: " + + e.getMessage()); + } + } + + // ===== 聊天编辑 ===== + public boolean isEditing(String name) { + return chatWait.contains(name); + } + + public void editTitle( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "title"); + p.closeInventory(); + p.sendMessage("§e请输入菜单标题:"); + } + + public void editCommand( + Player p, int idx) { + chatIdx.put(p.getName(), idx); + chatWait.add(p.getName()); + chatField.put(p.getName(), "command"); + p.closeInventory(); + p.sendMessage("§e请输入指令" + + "(自动补全/):"); + } + + public void onChat(Player p, String input) { + String name = p.getName(); + if (!chatWait.remove(name)) return; + + String field = chatField.remove(name); + Integer idx = chatIdx.get(name); + if (idx == null || idx >= items.size()) return; + + MenuItem mi = items.get(idx); + + if ("title".equals(field)) { + mi.title = input; + p.sendMessage("§a标题: " + input); + } else if ("command".equals(field)) { + if (!input.startsWith("/")) + input = "/" + input; + mi.command = input; + p.sendMessage("§a指令: " + input); + } + + Bukkit.getScheduler().runTask(plugin, () -> + plugin.gui.openEditor(p, idx)); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/OrderManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/OrderManager.java new file mode 100644 index 0000000..9dcb625 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/OrderManager.java @@ -0,0 +1,1345 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BookMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.logging.Level; + +public class OrderManager implements Listener { + private final Main plugin; + private final List orders = + Collections.synchronizedList(new ArrayList<>()); + private final Map userViewPage = new HashMap<>(); + private final Map adminViewPage = new HashMap<>(); + private final Map adminViewTarget = new HashMap<>(); + private final Set partialRefundListening = new HashSet<>(); + private final Map partialRefundTarget = new HashMap<>(); + private final Map receiptBuilderSection = new HashMap<>(); + private String tplHeader = ""; + private String tplItemLine = "{品名} x{数量} {原价}→{执行价} 小计{小计}枚"; + private String tplDiscountLine = "优惠: -{优惠额}枚 ({优惠类型})"; + private String tplPackLine = "打包: +{打包费}枚 ({打包颜色})"; + private String tplSummary = "原价{原价合计} → 实付{实付}"; + private String tplFooter = "感谢惠顾,欢迎再来!"; + private static final long RETENTION_30D = 30L * 24 * 60 * 60 * 1000; + private static final long SELF_REFUND_WINDOW = 5 * 60 * 1000; + private static final int PAGE_SIZE = 21; + private static final int[] SLOTS = { + 10,11,12,13,14,15,16,19,20,21,22,23,24,25,28,29,30,31,32,33,34}; + // ===== 字段 ===== + private final Set shulkerUsedOrders = + Collections.synchronizedSet(new HashSet<>()); + // ===== 待审批的退货退款 ===== + private final Map pendingReturns = + new HashMap<>(); + // ===== 退货申请确认 ===== + private final Map returnConfirmWaiting = + new HashMap<>(); // uuid -> orderId + private static final int RETURN_CONFIRM_TIMEOUT = 600; // 30秒 + + + private static class PendingReturn { + long orderId; + UUID playerUUID; + String playerName; + int totalPaid; + long createTime; + String packColor; + String packType; + List items; + + PendingReturn(OrderRecord r) { + this.orderId = r.orderId; + this.playerUUID = r.uuid; + this.playerName = r.player; + this.totalPaid = r.totalPaid; + this.createTime = System.currentTimeMillis(); + this.packColor = r.packColor; + this.packType = r.packType; + this.items = new ArrayList<>(r.items); + } + } + + + public static class OrderItem { + public String name; public String mat; + public int originalPrice; public int finalPrice; public int qty; + public OrderItem(String n,String m,int op,int fp,int q){ + name=n;mat=m;originalPrice=op;finalPrice=fp;qty=q;} + public int subtotal(){return finalPrice*qty;} + public int originalSubtotal(){return originalPrice*qty;} + } + public static class OrderRecord { + public long orderId; public UUID uuid; public String player; + public List items; public int totalOriginal; public int totalPaid; + public int discount; public String discountType; public String couponCode; + public int packFee; public String packType; public String packColor; + public long timestamp; public long printTime; public int status; + public int refundedAmount; public String refundType; + } + + public OrderManager(Main plugin) { + this.plugin = plugin; + loadReceiptConfig(); + loadOrders(); + startCleanupTask(); + Bukkit.getPluginManager().registerEvents(this, plugin); + } + public List getOrders() { return orders; } + + // ===== 小票模板 ===== + private void loadReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + if (!f.exists()) createDefaultReceiptConfig(f); + Map m = loadMap(f); + tplHeader = m.getOrDefault("头部", tplHeader); + tplItemLine = m.getOrDefault("商品行", tplItemLine); + tplDiscountLine = m.getOrDefault("优惠行", tplDiscountLine); + tplPackLine = m.getOrDefault("打包行", tplPackLine); + tplSummary = m.getOrDefault("汇总", tplSummary); + tplFooter = m.getOrDefault("页脚", tplFooter); + } + private void saveReceiptConfig() { + File f = new File(plugin.getDataFolder(), "小票设置.txt"); + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=" + tplHeader); + L.add("商品行=" + tplItemLine); + L.add("优惠行=" + tplDiscountLine); + L.add("打包行=" + tplPackLine); + L.add("汇总=" + tplSummary); + L.add("页脚=" + tplFooter); + writeLines(f, L); + } + private void createDefaultReceiptConfig(File f) { + List L = new ArrayList<>(); + L.add("# 小票模板配置"); + L.add("头部=§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}"); + L.add("商品行=§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚"); + L.add("优惠行=优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})"); + L.add("打包行=打包: §c+{打包费}枚§r ({打包颜色})"); + L.add("汇总=原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚"); + L.add("页脚=感谢惠顾,欢迎再来!"); + writeLines(f, L); + } + + // ===== 订单记录 ===== + public void recordOrder(Player p, List items, + int totalOriginal, int totalPaid, int discount, + String discountType, String couponCode, int packFee, + String packType, String packColor) { + OrderRecord r = new OrderRecord(); + r.orderId = System.currentTimeMillis(); + r.uuid = p.getUniqueId(); r.player = p.getName(); + r.items = items; r.totalOriginal = totalOriginal; + r.totalPaid = totalPaid; r.discount = discount; + r.discountType = discountType; r.couponCode = couponCode; + r.packFee = packFee; r.packType = packType; + r.packColor = packColor; r.timestamp = r.orderId; + r.printTime = 0; r.status = 0; r.refundedAmount = 0; + r.refundType = null; + orders.add(r); saveOrders(); + } + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + // ===== 小票聊天栏 ===== + /** + * 聊天栏打印小票(给定参数 Player + OrderRecord) + * 不要改动方法签名 + */ + public void sendReceiptChat(Player p, OrderRecord r) { + if (p == null || r == null) return; + + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + p.sendMessage("§7§m "); + p.sendMessage("§6§l======== 商店购物小票 ========"); + + // 退款标记 + String statusTag = getStatusTag(r); + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§7订单号: §f#" + r.orderId); + p.sendMessage("§7下单时间: §f" + + df.format(new Date(r.timestamp))); + p.sendMessage("§7玩家: §f" + r.player); + p.sendMessage("§7------------------------------"); + + for (OrderItem it : r.items) { + String price; + if (it.originalPrice != it.finalPrice) { + price = "§f" + it.originalPrice + + "§7→§6" + it.finalPrice; + } else { + price = "§6" + it.finalPrice; + } + p.sendMessage("§7 §e" + it.name + + " §7x" + it.qty + + " " + price + "枚" + + " §6小计" + it.subtotal() + "枚"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7原价: §f" + r.totalOriginal + "枚"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "§7(优惠券 -" + pct + "%)" + : "eco".equals(r.discountType) + ? "§7(环保 -" + pct + "%)" + : "§7(-" + pct + "%)"; + p.sendMessage("§7优惠: §a-" + r.discount + + "枚 " + dt); + } + + if (r.packFee > 0) { + String cn = r.packColor != null + ? r.packColor + .replace("_SHULKER_BOX", "") + .replace("SHULKER_BOX", "原色") + : "默认"; + p.sendMessage("§7打包: §c+" + r.packFee + + "枚 §7(" + cn + "潜影盒)"); + } else { + p.sendMessage("§7包装: 直接放入背包"); + } + + p.sendMessage("§7------------------------------"); + p.sendMessage("§7实付: §6" + r.totalPaid + "枚"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + p.sendMessage("§7余额: §a" + bal + "枚"); + + // 退款标记 + if (!statusTag.isEmpty()) { + p.sendMessage(statusTag); + } + + p.sendMessage("§6§l=============================="); + r.printTime = System.currentTimeMillis(); + } + + /** + * 生成书本小票内容 + */ + private String buildReceiptText(OrderRecord r) { + String statusTag = getStatusTag(r); + SimpleDateFormat df = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + StringBuilder sb = new StringBuilder(); + + sb.append("§6§l===== 商店小票 =====\n"); + + // 退款标记 + String tag = getStatusTag(r); + if (!tag.isEmpty()) sb.append(tag).append("\n"); + + sb.append("订单号: #").append(r.orderId).append("\n"); + sb.append("时间: ").append( + df.format(new Date(r.timestamp))).append("\n"); + sb.append("玩家: ").append(r.player).append("\n"); + sb.append("------------------------------\n"); + + for (OrderItem it : r.items) { + String pr = (it.originalPrice != it.finalPrice) + ? it.originalPrice + "->" + it.finalPrice + : String.valueOf(it.finalPrice); + sb.append(it.name).append(" x").append(it.qty) + .append(" ").append(pr).append("枚") + .append(" 小计").append(it.subtotal()) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("原价: ").append(r.totalOriginal) + .append("枚\n"); + + if (r.discount > 0) { + int pct = (r.totalOriginal > 0) + ? (r.discount * 100 / r.totalOriginal) + : 0; + String dt = "coupon".equals(r.discountType) + ? "优惠券(-" + pct + "%)" + : "环保(-" + pct + "%)"; + sb.append("优惠: -").append(r.discount) + .append("枚 ").append(dt).append("\n"); + } + + if (r.packFee > 0) { + sb.append("打包: +").append(r.packFee) + .append("枚\n"); + } + + sb.append("------------------------------\n"); + sb.append("实付: ").append(r.totalPaid).append("枚\n"); + + int bal = 0; + try { + bal = plugin.getBonds().getBonds(r.player); + } catch (Exception ignored) {} + sb.append("余额: ").append(bal).append("枚\n"); + + // 退款标记 + if (!statusTag.isEmpty()) sb.append("\n").append(tag).append("\n"); + + return sb.toString(); + } + + /** + * 退款状态标记文本 + */ + private String getStatusTag(OrderRecord r) { + switch (r.status) { + case 2: + return "§c§l<<<<<<< 已全额退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 3: + return "§c§l<<<<<<< 已部分退款 >>>>>>>" + + "\n§c退款金额: " + + r.refundedAmount + "枚"; + case 4: + return "§c§l<<<<<<< 退款申请已拒绝 >>>>>>>"; + default: + return ""; + } + } + + /** + * 创建小票书 + */ + public ItemStack createReceiptBook(OrderRecord r) { + String text = buildReceiptText(r); + String[] lines = text.split("\n"); + + ItemStack book = new ItemStack(Material.WRITTEN_BOOK); + BookMeta bm = (BookMeta) book.getItemMeta(); + bm.setTitle("购物小票 #" + r.orderId); + bm.setAuthor("商店"); + + StringBuilder page = new StringBuilder(); + int lineCount = 0; + for (String line : lines) { + // ★ 保留颜色代码 + page.append(line).append("\n"); + lineCount++; + if (lineCount >= 13) { + bm.addPage(page.toString().trim()); + page = new StringBuilder(); + lineCount = 0; + } + } + if (page.length() > 0) { + bm.addPage(page.toString().trim()); + } + + book.setItemMeta(bm); + return book; + } + + // ===== 退款工具 ===== + private boolean withinSelfRefundWindow(OrderRecord r) { return System.currentTimeMillis() - r.timestamp < SELF_REFUND_WINDOW; } + private int countDirectItems(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private int countShulkers(Player p, String matName) { + Material m = Material.matchMaterial(matName); if (m == null) return 0; + int c = 0; for (ItemStack is : p.getInventory().getContents()) if (is != null && is.getType() == m) c += is.getAmount(); + return c; + } + private void removeItems(Player p, String matName, int amt) { + Material m = Material.matchMaterial(matName); if (m != null) p.getInventory().removeItem(new ItemStack(m, amt)); + } + private boolean removeOneShulker(Player p, + String materialName) { + // 先尝试精确匹配 + Material mat = Material.matchMaterial(materialName); + if (mat != null) { + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + return true; + } + } + } + // 匹配不到,移除任意一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .endsWith("_SHULKER_BOX")) { + p.getInventory().clear(i); + return true; + } + } + return false; + } + public String validateRefund(OrderRecord r, Player p) { + if (r.status != 0) + return "订单状态: " + getStatusName(r.status); + + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // ★ 检查:潜影盒是否被放置过 + if (shulkerUsedOrders.contains(r.orderId)) { + return "§c潜影盒已被使用过,无法自助退款"; + } + + // ★ 检查:背包里是否有同一订单的潜影盒 + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im == null || im.getLore() == null) + continue; + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + return null; // 找到了,可退 + } + } + } + return "§c对应的潜影盒已不在背包中"; + } else { + // ★ 散装:检测物品数量 + for (OrderItem it : r.items) { + int have = countDirectItems(p, it.mat); + if (have < it.qty) + return "§c" + it.name + "不足(有" + + have + "/" + it.qty + ")"; + } + } + return null; + } + + + + private void notifyPlayer(UUID uuid, String msg) { Player p = Bukkit.getPlayer(uuid); if (p != null && p.isOnline()) p.sendMessage(msg); } + private void notifyAdmins(Player p, OrderRecord r) { + String m = "§e[订单] " + p.getName() + " 申请退款 #" + r.orderId + " §6" + r.totalPaid + "枚"; + for (Player a : Bukkit.getOnlinePlayers()) if (isAdmin(a) && !a.equals(p)) a.sendMessage(m); + } + private String getStatusName(int s) { switch(s){case 0:return "正常";case 1:return "申请中";case 2:return "已退";case 3:return "部分退";case 4:return "已拒绝";default:return "未知";} } + private boolean isAdmin(Player p) { + if (p.isOp()) return true; + try { ConfigManager cm = plugin.getConfigMgr(); if (cm == null) return false; return p.hasPermission("sdf1.admin") || p.hasPermission("sdf1." + cm.adminTag); } catch (Exception e) { return p.hasPermission("sdf1.admin"); } + } + + // ===== 订单中心 ===== + public void openOrderCenter(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l订单中心"); + g.setItem(13, mkItem(Material.BOOK, "§e§l购买记录", "§7查看历史订单", "", "§a点击进入")); + if (isAdmin(p)) { g.setItem(31, mkItem(Material.NETHER_STAR, "§c§l管理员面板", "§7在线玩家购物记录", "§7支持退款操作", "", "§a点击进入")); + g.setItem(33, mkItem(Material.CRAFTING_TABLE, "§e§l小票配置", "§7配置小票模板样式", "", "§a点击进入")); } + g.setItem(49, mkItem(Material.ARROW, "§7返回商店")); + p.openInventory(g); + } + + // ===== 购买记录 ===== + public void openMyOrders(Player p, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l购买记录"); + List list = getPlayerAllOrders(p.getUniqueId()); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + userViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon; String st; String act; + if (r.status != 0) { icon = r.status == 1 ? Material.PAPER : Material.BARRIER; st = "§7状态: " + getStatusName(r.status); act = "§b左键: 补打小票"; } + else if (withinSelfRefundWindow(r)) { icon = Material.EMERALD; st = "§7状态: §a可退款(5分钟内)"; act = "§b左键: 补打小票\n§a右键: 退款退货\n§eShift+右键: 仅退款"; } + else { icon = Material.BOOK; st = "§7状态: §7已过自助退款期"; act = "§b左键: 补打小票\n§e右键: 申请退款退货\n§6Shift+右键: 申请仅退款"; } + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7原价: §f" + r.totalOriginal + "枚"); + if (r.discount > 0) lore.add("§7优惠: §a-" + r.discount + "枚"); + lore.add("§7实付: §6" + r.totalPaid + "枚"); lore.add(st); lore.add(""); + for (String line : act.split("\n")) lore.add(line); + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7暂无订单记录")); + addPageButtons(g, page, total); + p.openInventory(g); + } + + // ===== 管理员面板 ===== + public void openAdminPanel(Player p, int page) { + if (!isAdmin(p)) { p.sendMessage("§c权限不足"); return; } + Inventory g = Bukkit.createInventory(null, 54, "§c§l管理员订单面板"); + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int total = Math.max(1, (int) Math.ceil((double) online.length / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); adminViewPage.put(p.getUniqueId(), page); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, online.length); + // ★ 替换 openAdminPanel 中的物品创建部分 + for (int i = start; i < end; i++) { + int si = i - start; + if (si >= SLOTS.length) break; + Player t = online[i]; + List tl = getPlayerOrdersWithin( + t.getUniqueId(), RETENTION_30D); + int ts = 0; + for (OrderRecord r : tl) ts += r.totalPaid; + + ItemStack is = new ItemStack(Material.PLAYER_HEAD); + org.bukkit.inventory.meta.SkullMeta sm = + (org.bukkit.inventory.meta.SkullMeta) + is.getItemMeta(); + if (sm != null) { + sm.setOwner(t.getName()); // ★ 设置头颅皮肤 + sm.setDisplayName("§e" + t.getName()); + sm.setLore(Arrays.asList( + "§7订单: §f" + tl.size() + "笔", + "§7消费: §6" + ts + "枚", + "", "§a点击进入")); + is.setItemMeta(sm); // ★ 这行之前缺失了 + } + g.setItem(SLOTS[si], is); + } + + if (online.length == 0) g.setItem(31, mkItem(Material.BARRIER, "§7无在线玩家")); + addPageButtons(g, page, total); p.openInventory(g); + } + + // ===== 管理员订单 ===== + public void openAdminOrders(Player p, UUID targetUUID, int page) { + Inventory g = Bukkit.createInventory(null, 54, "§c§l订单管理"); + String name = Bukkit.getOfflinePlayer(targetUUID).getName(); + List list = getPlayerOrdersWithin(targetUUID, RETENTION_30D); + int total = Math.max(1, (int) Math.ceil((double) list.size() / PAGE_SIZE)); + page = Math.max(0, Math.min(page, total - 1)); + adminViewPage.put(p.getUniqueId(), page); adminViewTarget.put(p.getUniqueId(), targetUUID); + int start = page * PAGE_SIZE; int end = Math.min(start + PAGE_SIZE, list.size()); + for (int i = start; i < end; i++) { + int si = i - start; if (si >= SLOTS.length) break; + OrderRecord r = list.get(i); + Material icon = r.status == 2 || r.status == 4 ? Material.BARRIER : r.status == 1 ? Material.PAPER : Material.BOOK; + ItemStack is = new ItemStack(icon); ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e订单 #" + r.orderId); + List lore = new ArrayList<>(); lore.add("§7" + fmtDate(r.timestamp)); + for (OrderItem it : r.items) lore.add("§7 §e" + it.name + " §7x" + it.qty + " §6" + it.subtotal() + "枚"); + lore.add("§7─────────"); lore.add("§7实付: §6" + r.totalPaid + "枚"); + if (r.status == 1) { lore.add("§7状态: §e申请中(" + nn(r.refundType) + ")"); lore.add(""); lore.add("§a右键: 批准"); lore.add("§c左键: 拒绝"); } + else if (r.status == 2) lore.add("§7已退: §a" + r.refundedAmount + "枚"); + else if (r.status == 3) lore.add("§7部分退: §a" + r.refundedAmount + "枚"); + else if (r.status == 4) lore.add("§7已拒绝"); + else { lore.add(""); lore.add("§a右键: 整单退款"); lore.add("§e左键: 部分退款(输入%)"); } + im.setLore(lore); is.setItemMeta(im); + } + g.setItem(SLOTS[si], is); + } + if (list.isEmpty()) g.setItem(31, mkItem(Material.BARRIER, "§7该玩家暂无订单")); + addPageButtons(g, page, total); + g.setItem(49, mkItem(org.bukkit.Material.PAPER, name + " §e" + (page + 1) + "/" + total)); + p.openInventory(g); + } + private void addPageButtons(Inventory g, int page, int total) { + g.setItem(48, page > 0 ? mkItem(Material.ARROW, "§7上一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + if (g.getItem(49) == null) g.setItem(49, mkItem(Material.PAPER, "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 ? mkItem(Material.ARROW, "§7下一页") : mkItem(Material.BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, "§7返回")); + } + + // ===== 小票配置 ===== + public void openReceiptConfig(Player p) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l小票配置"); + g.setItem(10, mkItem(Material.PAPER, "§e§l头部", "§7" + brief(tplHeader), "", "§a点击编辑")); + g.setItem(11, mkItem(Material.PAPER, "§e§l商品行", "§7" + brief(tplItemLine), "", "§a点击编辑")); + g.setItem(12, mkItem(Material.PAPER, "§e§l优惠行", "§7" + brief(tplDiscountLine), "", "§a点击编辑")); + g.setItem(14, mkItem(Material.PAPER, "§e§l打包行", "§7" + brief(tplPackLine), "", "§a点击编辑")); + g.setItem(15, mkItem(Material.PAPER, "§e§l汇总", "§7" + brief(tplSummary), "", "§a点击编辑")); + g.setItem(16, mkItem(Material.PAPER, "§e§l页脚", "§7" + brief(tplFooter), "", "§a点击编辑")); + g.setItem(22, mkItem(Material.BOOK, "§6§l预览(随机价格)", "§7预览小票效果")); + g.setItem(48, mkItem(Material.LIME_WOOL, "§a§l保存")); g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(50, mkItem(Material.RED_WOOL, "§c§l重置")); + p.openInventory(g); + } + public void openReceiptBuilder(Player p, String section) { + Inventory g = Bukkit.createInventory(null, 54, "§6§l编辑: " + sectionName(section)); + g.setItem(10, mkItem(Material.EMERALD, "§a{品名}")); g.setItem(11, mkItem(Material.EMERALD, "§a{原价}")); + g.setItem(12, mkItem(Material.EMERALD, "§a{执行价}")); g.setItem(13, mkItem(Material.EMERALD, "§a{数量}")); + g.setItem(14, mkItem(Material.EMERALD, "§a{小计}")); g.setItem(15, mkItem(Material.EMERALD, "§a{商店名}")); + g.setItem(16, mkItem(Material.EMERALD, "§a{玩家}")); g.setItem(19, mkItem(Material.DIAMOND, "§b{订单号}")); + g.setItem(20, mkItem(Material.DIAMOND, "§b{订单时间}")); g.setItem(21, mkItem(Material.DIAMOND, "§b{打印时间}")); + g.setItem(22, mkItem(Material.DIAMOND, "§b{日期}")); g.setItem(23, mkItem(Material.DIAMOND, "§b{时间}")); + g.setItem(24, mkItem(Material.DIAMOND, "§b{原价合计}")); g.setItem(25, mkItem(Material.DIAMOND, "§b{实付}")); + g.setItem(28, mkItem(Material.GOLD_INGOT, "§6{优惠额}")); g.setItem(29, mkItem(Material.GOLD_INGOT, "§6{优惠类型}")); + g.setItem(30, mkItem(Material.GOLD_INGOT, "§6{优惠券码}")); g.setItem(31, mkItem(Material.GOLD_INGOT, "§6{打包费}")); + g.setItem(32, mkItem(Material.GOLD_INGOT, "§6{打包颜色}")); g.setItem(33, mkItem(Material.GOLD_INGOT, "§6{余额}")); + g.setItem(37, mkItem(Material.STONE, "§f═══")); g.setItem(38, mkItem(Material.STONE, "§f───")); + g.setItem(39, mkItem(Material.STONE, "§f═════════════")); g.setItem(40, mkItem(Material.PAPER, "§e换行(\\n)")); + g.setItem(47, mkItem(Material.RED_STAINED_GLASS_PANE, "§c删末尾")); g.setItem(48, mkItem(Material.RED_WOOL, "§c清空")); + g.setItem(49, mkItem(Material.ARROW, "§7返回")); g.setItem(50, mkItem(Material.LIME_WOOL, "§a保存")); + g.setItem(51, mkItem(Material.BLUE_WOOL, "§9重置")); + p.openInventory(g); + } + // ===== 点击处理 ===== + public boolean handleCenterClick(Player p, int raw) { + if (raw == 13) { openMyOrders(p, 0); return true; } + if (raw == 31 && isAdmin(p)) { openAdminPanel(p, 0); return true; } + if (raw == 33 && isAdmin(p)) { openReceiptConfig(p); return true; } + if (raw == 49) { plugin.getShopManager().openShopMain(p); return true; } + return true; + } + public boolean handleMyOrdersClick(Player p, int raw, boolean left, boolean shift) { + List list = getPlayerAllOrders(p.getUniqueId()); + int page = userViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openMyOrders(p, page - 1); return true; } + if (raw == 50) { openMyOrders(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (left) { + try { ItemStack book = createReceiptBook(r); + HashMap ov = p.getInventory().addItem(book); + for (ItemStack d : ov.values()) p.getWorld().dropItemNaturally(p.getLocation(), d); + r.printTime = System.currentTimeMillis(); p.sendMessage("§a已补打小票书: #" + r.orderId); + } catch (Exception e) { p.sendMessage("§c补打失败"); } + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // ★ Shift+右键:仅退款申请(需管理员审批) + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请,等待管理员审批"); + notifyAdmins(p, r); + } else { + // ★ 普通右键:5分钟内直接退(自动通过) + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason + + ",Shift+右键可申请仅退款"); + } else { + // 扣货 + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + if (isShulker) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory() + .getItem(i); + if (is != null && is.getType() + .name().contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + // 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + } + + + } else { + if (r.status != 0) { + p.sendMessage("§c状态: " + + getStatusName(r.status)); + } else if (withinSelfRefundWindow(r)) { + if (shift) { + // 仅退款申请 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } else { + // 5分钟内退货退款 + String reason = validateRefund(r, p); + if (reason != null) { + // ★ 不满足条件:询问是否申请 + p.sendMessage("§c" + reason); + askReturnConfirm(p, r); + } else { + // 满足条件:自动退 + doAutoReturn(p, r); + } + } + } else { + if (shift) { + // 申请仅退款 + r.status = 1; + r.refundType = "refund_only_apply"; + saveOrders(); + p.sendMessage("§e已提交仅退款申请"); + notifyAdmins(p, r); + } else { + // ★ 申请退货退款:先检查条件 + String reason = validateRefund(r, p); + if (reason != null) { + p.sendMessage("§c" + reason); + } + // ★ 无论是否满足条件都询问 + askReturnConfirm(p, r); + } + } + } + } + if (left) { + // 已退款订单仍然允许补打小票,但小票内会显示退款标记 + try { + ItemStack book = createReceiptBook(r); + HashMap overflow = + p.getInventory().addItem(book); + for (ItemStack drop : overflow.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + r.printTime = System.currentTimeMillis(); + if (r.status != 0) { + p.sendMessage("§e已补打小票(含退款标记): #" + + r.orderId); + } else { + p.sendMessage("§a已补打小票书: #" + + r.orderId); + } + } catch (Exception e) { + p.sendMessage("§c补打失败"); + } + } + + final int fp = page; Bukkit.getScheduler().runTask(plugin, () -> openMyOrders(p, fp)); + return true; + } + public boolean handleAdminClick(Player p, int raw) { + Player[] online = Bukkit.getOnlinePlayers().toArray(new Player[0]); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { openAdminPanel(p, page - 1); return true; } + if (raw == 50) { openAdminPanel(p, page + 1); return true; } + if (raw == 53) { openOrderCenter(p); return true; } + int si = slotIndex(raw); if (si < 0) return true; + int actual = si + page * PAGE_SIZE; if (actual < 0 || actual >= online.length) return true; + openAdminOrders(p, online[actual].getUniqueId(), 0); return true; + } + public boolean handleAdminOrdersClick(Player p, int raw, boolean left, boolean shift) { + UUID tu = adminViewTarget.get(p.getUniqueId()); + if (tu == null) return false; + List list = getPlayerOrdersWithin(tu, RETENTION_30D); + int page = adminViewPage.getOrDefault(p.getUniqueId(), 0); + if (raw == 48) { + openAdminOrders(p, tu, page - 1); + return true; + } + if (raw == 50) { + openAdminOrders(p, tu, page + 1); + return true; + } + if (raw == 53) { + openAdminPanel(p, 0); + return true; + } + int si = slotIndex(raw); + if (si < 0) return true; + int actual = si + page * PAGE_SIZE; + if (actual < 0 || actual >= list.size()) return true; + OrderRecord r = list.get(actual); + if (r.status == 2 || r.status == 3 || r.status == 4) { + p.sendMessage("§c状态: " + getStatusName(r.status)); + return true; + } + if (left) { + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款被拒绝"); + openAdminOrders(p, tu, page); + } else if (r.status == 0) { + partialRefundListening.add(p.getUniqueId()); + partialRefundTarget.put(p.getUniqueId(), r); + p.closeInventory(); + p.sendMessage("§e输入退款百分比(1-100): #" + r.orderId + " 实付" + r.totalPaid + "枚"); + } + } else { + if (r.status == 0 || r.status == 1) { + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a全额退款 §e" + r.player + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, "§a退款 §6" + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + } + } + // ★ 替换 handleAdminOrdersClick 中的审批逻辑 + if (left) { + // 左键 = 拒绝 + if (r.status == 1) { + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款: #" + r.orderId); + notifyPlayer(r.uuid, "§c退款申请已被拒绝"); + openAdminOrders(p, tu, page); + } + } else { + // 右键 = 批准 + if (r.status == 1) { + String rt = r.refundType != null + ? r.refundType : ""; + + // ★ 退款退货申请 → 检查潜影盒 + if (rt.equals("refund_return_apply")) { + String pt = r.packType != null + ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 检查玩家背包里有没有带订单标签的盒子 + Player target = Bukkit.getPlayer(r.uuid); + if (target == null || !target.isOnline()) { + p.sendMessage("§c玩家不在线,无法执行退货退款"); + openAdminOrders(p, tu, page); + return true; + } + + boolean foundTagged = false; + boolean foundAny = false; + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is == null) continue; + if (!is.getType().name() + .contains("SHULKER")) continue; + foundAny = true; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + foundTagged = true; + // 收走盒子 + target.getInventory().clear(i); + break; + } + } + } + if (foundTagged) break; + } + + if (foundTagged) { + // ★ 有带标记的盒子 → 收走 + 退钱 + plugin.getBonds().addBonds( + r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + + r.player + " §6" + + r.totalPaid + "枚(盒子已收走)"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + return true; + } else if (foundAny) { + // ★ 有盒子但没标记 → 等管理员决定 + pendingReturns.put(r.orderId, + new PendingReturn(r)); + p.closeInventory(); + p.sendMessage("§e§l[退货审批] 订单 #" + + r.orderId); + p.sendMessage("§7玩家背包有潜影盒但" + + "无订单标记,可能已被替换"); + p.sendMessage("§a输入 §e1 §a= 收走盒子并退款"); + p.sendMessage("§e输入 §e2 §e= 仅退款(不收盒子)"); + p.sendMessage("§c输入 §c3 §c= 拒绝退款"); + p.sendMessage("§72分钟内未操作自动拒绝"); + // 启动2分钟超时 + startReturnTimeout(r.orderId); + return true; + } else { + // ★ 背包里没有任何盒子 + p.sendMessage("§c玩家背包里没有潜影盒"); + p.sendMessage("§7可拒绝并通知玩家" + + "申请仅退款或把货拿回来"); + openAdminOrders(p, tu, page); + return true; + } + } + // ★ 散装退货退款:扣货+退钱 + Player target = Bukkit.getPlayer(r.uuid); + if (target != null && target.isOnline()) { + for (OrderItem it : r.items) + removeItems(target, it.mat, it.qty); + } + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退货退款成功,§6" + + r.totalPaid + "枚"); + openAdminOrders(p, tu, page); + return true; + } + + // ★ 仅退款申请 / 5分钟内自动退款 + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + saveOrders(); + p.sendMessage("§a已批准退款 §e" + r.player + + " §6" + r.totalPaid + "枚"); + notifyPlayer(r.uuid, + "§a[订单] 退款已批准,§6" + + r.totalPaid + "枚已退还"); + openAdminOrders(p, tu, page); + } + } + return true; + } + + /** + * 询问用户是否提交退货退款申请 + */ + private void askReturnConfirm(Player p, OrderRecord r) { + returnConfirmWaiting.put(p.getUniqueId(), r.orderId); + p.sendMessage(""); + p.sendMessage("§e§l[退货退款申请]"); + p.sendMessage("§7订单 #" + r.orderId + + " 实付" + r.totalPaid + "枚"); + p.sendMessage("§a输入 §e1 §a= 提交退货退款申请"); + p.sendMessage("§c输入 §e2 §c= 取消"); + p.sendMessage("§730秒内未操作自动取消"); + p.sendMessage(""); + + // 30秒超时 + final UUID uuid = p.getUniqueId(); + final long oid = r.orderId; + new BukkitRunnable() { + @Override + public void run() { + Long waiting = returnConfirmWaiting.get(uuid); + if (waiting != null && waiting == oid) { + returnConfirmWaiting.remove(uuid); + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§7退货退款申请已超时取消"); + } + } + } + }.runTaskLater(plugin, RETURN_CONFIRM_TIMEOUT); + } + + /** + * 执行5分钟内自动退货退款 + */ + private void doAutoReturn(Player p, OrderRecord r) { + String pt = r.packType != null ? r.packType : "none"; + boolean isShulker = pt.equals("custom") + || pt.equals("default") + || pt.equals("shulker"); + + if (isShulker) { + // 找并收走带标签的盒子 + boolean found = false; + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is == null) continue; + if (!is.getType().name().contains("SHULKER")) + continue; + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + r.orderId)) { + p.getInventory().clear(i); + found = true; + break; + } + } + } + if (found) break; + } + // 没找到标签盒子,收任意潜影盒 + if (!found) { + for (int i = 0; i < 36; i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + p.getInventory().clear(i); + break; + } + } + } + } else { + for (OrderItem it : r.items) + removeItems(p, it.mat, it.qty); + } + + plugin.getBonds().addBonds(r.player, r.totalPaid); + r.status = 2; + r.refundedAmount = r.totalPaid; + r.refundType = "auto_return"; + saveOrders(); + p.sendMessage("§a退款退货成功: §6" + + r.totalPaid + "枚"); + } + + + // ===== 2分钟超时 ===== + private void startReturnTimeout(long orderId) { + new BukkitRunnable() { + @Override + public void run() { + PendingReturn pr = pendingReturns + .remove(orderId); + if (pr == null) return; // 已处理 + Player admin = Bukkit.getPlayer(pr.playerUUID); + if (admin != null && admin.isOnline()) { + admin.sendMessage("§c[退货审批] 订单 #" + + orderId + " 超时,自动拒绝"); + } + // 通知玩家 + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款申请已超时拒绝"); + // 恢复订单状态 + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 0; + r.refundType = null; + saveOrders(); + } + } + }.runTaskLater(plugin, 2400L); // 2分钟 + } + + // ===== 查找订单 ===== + private OrderRecord findOrder(long orderId) { + synchronized (orders) { + for (OrderRecord r : orders) { + if (r.orderId == orderId) return r; + } + } + return null; + } + + + public boolean handleReceiptConfigClick(Player p, int raw) { + if (raw == 49) { openOrderCenter(p); return true; } + if (raw == 48) { saveReceiptConfig(); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 50) { createDefaultReceiptConfig(new File(plugin.getDataFolder(), "小票设置.txt")); loadReceiptConfig(); p.sendMessage("§e已重置"); openReceiptConfig(p); return true; } + if (raw == 22) { sendRandomPreview(p); return true; } + String sec = null; + if (raw == 10) sec = "header"; else if (raw == 11) sec = "itemLine"; else if (raw == 12) sec = "discountLine"; + else if (raw == 14) sec = "packLine"; else if (raw == 15) sec = "summary"; else if (raw == 16) sec = "footer"; + if (sec != null) { receiptBuilderSection.put(p.getUniqueId(), sec); openReceiptBuilder(p, sec); } + return true; + } + public boolean handleReceiptBuilderClick(Player p, int raw) { + String sec = receiptBuilderSection.get(p.getUniqueId()); if (sec == null) return false; + String token = getTokenBySlot(raw); + if (token != null) { String cur = getSection(sec); setSection(sec, cur.isEmpty() ? token : cur + token); openReceiptBuilder(p, sec); return true; } + if (raw == 47) { String c = getSection(sec); int lb = c.lastIndexOf('}'); if (lb >= 0) { int ls = c.lastIndexOf('{', lb); if (ls >= 0) setSection(sec, c.substring(0, ls)); } else setSection(sec, c.substring(0, Math.max(0, c.length() - 8))); openReceiptBuilder(p, sec); return true; } + if (raw == 48) { setSection(sec, ""); openReceiptBuilder(p, sec); return true; } + if (raw == 49) { receiptBuilderSection.remove(p.getUniqueId()); openReceiptConfig(p); return true; } + if (raw == 50) { saveReceiptConfig(); receiptBuilderSection.remove(p.getUniqueId()); p.sendMessage("§a已保存"); openReceiptConfig(p); return true; } + if (raw == 51) { setSection(sec, getDefaultVal(sec)); openReceiptBuilder(p, sec); return true; } + return true; + } + + // ===== 聊天事件 ===== + @EventHandler + public void onAdminChat(AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); UUID uuid = p.getUniqueId(); + // ★ 在 onAdminChat 方法开头添加 +// ===== 退货审批输入 ===== + for (Map.Entry entry : + new HashMap<>(pendingReturns).entrySet()) { + Long pendingOrderId = entry.getKey(); + PendingReturn pr = entry.getValue(); + if (pr.playerUUID.equals(p.getUniqueId()) + || isAdmin(p)) { + try { + int choice = Integer.parseInt( + e.getMessage().trim()); + pendingReturns.remove(pendingOrderId); + OrderRecord r = findOrder(pendingOrderId); + if (r == null) { + p.sendMessage("§c订单不存在"); + return; + } + + if (choice == 1) { + // ★ 收走盒子并退款 + Player target = Bukkit.getPlayer( + pr.playerUUID); + if (target != null && target.isOnline()) { + for (int i = 0; i < 36; i++) { + ItemStack is = target.getInventory() + .getItem(i); + if (is != null && is.getType().name() + .contains("SHULKER")) { + ItemMeta im = is.getItemMeta(); + if (im != null && im.getLore() != null) { + for (String line : im.getLore()) { + if (line.equals("§8#" + pendingOrderId)) { + target.getInventory().clear(i); + break; + } + } + } + break; + } + } + } + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§a已退货退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(盒子已收走)"); + notifyPlayer(pr.playerUUID, + "§a[订单] 退货退款成功,§6" + + pr.totalPaid + "枚已退还"); + + } else if (choice == 2) { + // ★ 仅退款(不收盒子) + plugin.getBonds().addBonds(pr.playerName, pr.totalPaid); + r.status = 2; + r.refundedAmount = pr.totalPaid; + saveOrders(); + p.sendMessage("§e已仅退款 §e" + pr.playerName + + " §6" + pr.totalPaid + "枚(未收盒子)"); + notifyPlayer(pr.playerUUID, + "§e[订单] 仅退款成功,§6" + + pr.totalPaid + "枚已退还" + + ",盒子无需退回"); + + } else if (choice == 3) { + // ★ 拒绝 + r.status = 0; + r.refundType = null; + saveOrders(); + p.sendMessage("§c已拒绝退款 #" + pendingOrderId); + notifyPlayer(pr.playerUUID, + "§c[订单] 退货退款已被拒绝" + + ",您可以申请仅退款" + + "或把货拿回来后重新申请"); + } else { + p.sendMessage("§c请输入 1、2 或 3"); + pendingReturns.put(pendingOrderId, pr); + } + return; + + } catch (NumberFormatException ignored) { + // 不是数字,跳过 + } + } + } +// ★ 在 onAdminChat 方法开头,处理用户退货确认 + if (returnConfirmWaiting.containsKey(uuid)) { + String msg = e.getMessage().trim(); + if (msg.equals("1")) { + // ★ 确认提交 + Long orderId = returnConfirmWaiting.remove(uuid); + if (orderId != null) { + OrderRecord r = findOrder(orderId); + if (r != null) { + r.status = 1; + r.refundType = "refund_return_apply"; + saveOrders(); + p.sendMessage("§e已提交退货退款申请," + + "等待管理员审批"); + notifyAdmins(p, r); + } + } + e.setCancelled(true); + return; + } else if (msg.equals("2")) { + // ★ 取消 + returnConfirmWaiting.remove(uuid); + p.sendMessage("§7已取消退货退款申请"); + e.setCancelled(true); + return; + } else { + // 输入其他内容:取消等待 + returnConfirmWaiting.remove(uuid); + // 不 return,让消息正常发送 + } + } + + if (partialRefundListening.contains(uuid)) { + e.setCancelled(true); partialRefundListening.remove(uuid); + OrderRecord t = partialRefundTarget.remove(uuid); if (t == null) return; + try { int pct = Integer.parseInt(e.getMessage().trim()); if (pct < 1 || pct > 100) { p.sendMessage("§c1-100"); return; } + int ref = t.totalPaid * pct / 100; if (ref <= 0) ref = 1; + plugin.getBonds().addBonds(t.player, ref); t.status = 3; t.refundedAmount = ref; saveOrders(); + p.sendMessage("§a部分退款 §e" + t.player + " §6" + ref + "枚(" + pct + "%)"); + notifyPlayer(t.uuid, "§a退款 §6" + ref + "枚"); + final UUID tu = adminViewTarget.get(uuid); final int pg = adminViewPage.getOrDefault(uuid, 0); + Bukkit.getScheduler().runTask(plugin, () -> openAdminOrders(p, tu, pg)); + } catch (NumberFormatException ex) { p.sendMessage("§c请输入数字"); } catch (Exception ex) { plugin.getLogger().log(Level.WARNING, "[Order]退款异常", ex); } + return; + } + if (receiptBuilderSection.containsKey(uuid)) { + e.setCancelled(true); String sec = receiptBuilderSection.remove(uuid); if (sec == null) return; + String msg = e.getMessage().trim(); + if ("cancel".equalsIgnoreCase(msg)) { Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); return; } + setSection(sec, msg.replace("\\n", "\n")); p.sendMessage("§a已更新 §e" + sec); + Bukkit.getScheduler().runTask(plugin, () -> openReceiptConfig(p)); + } + } + + // ===== 数据查询 ===== + private List getPlayerAllOrders(UUID uuid) { + List r = new ArrayList<>(); + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && System.currentTimeMillis() - o.timestamp < RETENTION_30D) r.add(o); } + Collections.reverse(r); return r; + } + private List getPlayerOrdersWithin(UUID uuid, long ms) { + List r = new ArrayList<>(); long cut = System.currentTimeMillis() - ms; + synchronized (orders) { for (OrderRecord o : orders) if (o.uuid.equals(uuid) && o.timestamp >= cut) r.add(o); } + Collections.reverse(r); return r; + } + + // ===== 持久化 ===== + private void loadOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); if (!f.exists()) return; + try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); + OrderRecord cur = null; String line; + while ((line = br.readLine()) != null) { line = line.trim(); + if (line.equals("==")) { cur = new OrderRecord(); cur.items = new ArrayList<>(); } + else if (cur != null && line.contains("=")) { int eq = line.indexOf('='); String k = line.substring(0, eq); String v = line.substring(eq + 1); + switch (k) { case "id": cur.orderId = Long.parseLong(v); cur.timestamp = cur.orderId; break; case "uuid": cur.uuid = UUID.fromString(v); break; + case "player": cur.player = v; break; case "totalOriginal": cur.totalOriginal = Integer.parseInt(v); break; case "totalPaid": cur.totalPaid = Integer.parseInt(v); break; + case "discount": cur.discount = Integer.parseInt(v); break; case "discountType": cur.discountType = v; break; case "couponCode": cur.couponCode = v; break; + case "packFee": cur.packFee = Integer.parseInt(v); break; case "packType": cur.packType = v; break; case "packColor": cur.packColor = v; break; + case "status": cur.status = Integer.parseInt(v); break; case "refundedAmount": cur.refundedAmount = Integer.parseInt(v); break; + case "printTime": cur.printTime = Long.parseLong(v); break; case "refundType": cur.refundType = v; break; + case "item": String[] pp = v.split("\\|"); if (pp.length >= 5) cur.items.add(new OrderItem(pp[0], pp[1], Integer.parseInt(pp[2]), Integer.parseInt(pp[3]), Integer.parseInt(pp[4]))); break; } + } else if (line.isEmpty() && cur != null && cur.uuid != null) { orders.add(cur); cur = null; } + } if (cur != null && cur.uuid != null) orders.add(cur); br.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]加载失败: " + e.getMessage()); } + } + private void saveOrders() { + File f = new File(plugin.getDataFolder(), "orders.dat"); + try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); + synchronized (orders) { for (OrderRecord r : orders) { pw.println("=="); + pw.println("id=" + r.orderId); pw.println("uuid=" + r.uuid); pw.println("player=" + r.player); + pw.println("totalOriginal=" + r.totalOriginal); pw.println("totalPaid=" + r.totalPaid); + pw.println("discount=" + r.discount); pw.println("discountType=" + nn(r.discountType)); + pw.println("couponCode=" + nn(r.couponCode)); pw.println("packFee=" + r.packFee); + pw.println("packType=" + nn(r.packType)); pw.println("packColor=" + nn(r.packColor)); + pw.println("status=" + r.status); pw.println("refundedAmount=" + r.refundedAmount); + pw.println("printTime=" + r.printTime); pw.println("refundType=" + nn(r.refundType)); + for (OrderItem it : r.items) pw.println("item=" + it.name + "|" + it.mat + "|" + it.originalPrice + "|" + it.finalPrice + "|" + it.qty); + } } pw.flush(); pw.close(); + } catch (Exception e) { plugin.getLogger().warning("[Order]保存失败: " + e.getMessage()); } + } + private void startCleanupTask() { new BukkitRunnable() { public void run() { synchronized (orders) { if (orders.removeIf(OrderManager.this::isExpired)) saveOrders(); } } }.runTaskTimer(plugin, 36000L, 36000L); } + /** + * 将小票书塞入潜影盒 + */ + public void addBookToShulker(ItemStack shulker, ItemStack book) { + try { + org.bukkit.inventory.meta.BlockStateMeta bm = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (bm == null) return; + org.bukkit.block.BlockState st = bm.getBlockState(); + if (st instanceof org.bukkit.block.Container) { + ((org.bukkit.block.Container) st) + .getInventory().addItem(book); + bm.setBlockState(st); + shulker.setItemMeta(bm); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Order] 塞书失败: " + e.getMessage()); + } + } + + // ===== 工具 ===== + private String nn(String s) { return s != null ? s : ""; } + private boolean isExpired(OrderRecord r) { return System.currentTimeMillis() - r.timestamp > RETENTION_30D; } + private String fmtDate(long ts) { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(ts)); } + private int slotIndex(int raw) { for (int i = 0; i < SLOTS.length; i++) if (SLOTS[i] == raw) return i; return -1; } + private String brief(String s) { if (s == null || s.isEmpty()) return "(空)"; String c = sc(s); return c.length() > 30 ? c.substring(0, 30) + "..." : c; } + private String sectionName(String s) { switch(s){case "header":return "头部";case "itemLine":return "商品行";case "discountLine":return "优惠行";case "packLine":return "打包行";case "summary":return "汇总";case "footer":return "页脚";default:return s;} } + private String sc(String s) { + if (s == null) return ""; + return s.replaceAll("§[0-9a-fk-orA-FK-OR]", ""); + } + private String getSection(String s) { switch(s){case "header":return tplHeader;case "itemLine":return tplItemLine;case "discountLine":return tplDiscountLine;case "packLine":return tplPackLine;case "summary":return tplSummary;case "footer":return tplFooter;default:return "";} } + private void setSection(String s, String v) { switch(s){case "header":tplHeader=v;break;case "itemLine":tplItemLine=v;break;case "discountLine":tplDiscountLine=v;break;case "packLine":tplPackLine=v;break;case "summary":tplSummary=v;break;case "footer":tplFooter=v;break;} } + private String getDefaultVal(String s) { switch(s){case "header":return "§6§l{商店名}§r\n购物小票\n订单#{订单号} {订单时间}\n玩家: {玩家}";case "itemLine":return "§e{品名}§r x{数量} {原价}→{执行价} 小计{小计}枚";case "discountLine":return "优惠: §a-{优惠额}枚§r ({优惠类型}{优惠券码})";case "packLine":return "打包: §c+{打包费}枚§r ({打包颜色})";case "summary":return "原价{原价合计}枚 → 实付§6{实付}枚§r 余额{余额}枚";case "footer":return "感谢惠顾,欢迎再来!";default:return "";} } + private String getTokenBySlot(int raw) { switch(raw){case 10:return "{品名}";case 11:return "{原价}";case 12:return "{执行价}";case 13:return "{数量}";case 14:return "{小计}";case 15:return "{商店名}";case 16:return "{玩家}";case 19:return "{订单号}";case 20:return "{订单时间}";case 21:return "{打印时间}";case 22:return "{日期}";case 23:return "{时间}";case 24:return "{原价合计}";case 25:return "{实付}";case 28:return "{优惠额}";case 29:return "{优惠类型}";case 30:return "{优惠券码}";case 31:return "{打包费}";case 32:return "{打包颜色}";case 33:return "{余额}";case 37:return "═══";case 38:return "───";case 39:return "═════════════";case 40:return "\\n";default:return null;} } + private ItemStack mkItem(Material mat, String name, String... lore) { ItemStack it = new ItemStack(mat); ItemMeta im = it.getItemMeta(); if (im != null) { im.setDisplayName(name); if (lore.length > 0) im.setLore(Arrays.asList(lore)); it.setItemMeta(im); } return it; } + private Map loadMap(File f) { Map m = new LinkedHashMap<>(); try { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)); String line; while ((line = br.readLine()) != null) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) continue; int eq = line.indexOf('='); if (eq > 0) m.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim()); } br.close(); } catch (Exception ignored) {} return m; } + private void writeLines(File f, List lines) { try { PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)); for (String l : lines) pw.println(l); pw.flush(); pw.close(); } catch (Exception ignored) {} } + private void sendRandomPreview(Player p) { String[] n = {"钻石剑","钻石镐","铁剑","金苹果"}; String[] m = {"DIAMOND_SWORD","DIAMOND_PICKAXE","IRON_SWORD","GOLDEN_APPLE"}; Random r = new Random(); List items = new ArrayList<>(); for (int i = 0; i < 2 + r.nextInt(3); i++) { int idx = r.nextInt(n.length); items.add(new OrderItem(n[idx], m[idx], 100 + r.nextInt(5000), 100 + r.nextInt(5000), 1 + r.nextInt(10))); } OrderRecord o = new OrderRecord(); o.orderId = System.currentTimeMillis(); o.uuid = p.getUniqueId(); o.player = p.getName(); o.items = items; o.totalOriginal = 0; for (OrderItem it : items) o.totalOriginal += it.originalSubtotal(); o.totalPaid = o.totalOriginal; o.timestamp = o.orderId; o.status = 0; p.sendMessage("§6§l===== 小票预览(随机) ====="); sendReceiptChat(p, o); p.sendMessage("§6§l===== 预览结束 ====="); } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/PVPManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PVPManager.java new file mode 100644 index 0000000..326c0fb --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PVPManager.java @@ -0,0 +1,1380 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.scoreboard.*; + + +import java.io.File; +import java.sql.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class PVPManager implements Listener { + + private final Main plugin; + private Connection pvpDb; + + private final Map + regions = new ConcurrentHashMap<>(); + private final Map + inRegion = new ConcurrentHashMap<>(); + private final Map + sessionKills = + new ConcurrentHashMap<>(); + private final Map + lastKillTime = + new ConcurrentHashMap<>(); + private final Map + selectState = + new ConcurrentHashMap<>(); + private final Map + killSessions = + new ConcurrentHashMap<>(); + private final Set tripleAnnounced = + ConcurrentHashMap.newKeySet(); + private final Map lastScoreboardRefresh = new ConcurrentHashMap<>(); + + + // 边框显示 + private final Set + permanentBorders = + ConcurrentHashMap.newKeySet(); + private final Set + tempBorders = + ConcurrentHashMap.newKeySet(); + + + public static class KillSession { + public int count = 0; + public long firstKill = 0; + public long lastKill = 0; + public boolean tripleAnnounced = + false; + public String region = ""; + } + + public static class PVPRegion { + public String name; + public String world; + public int x1, y1, z1; + public int x2, y2, z2; + public int minX, minY, minZ; + public int maxX, maxY, maxZ; + + public void calcBounds() { + minX = Math.min(x1, x2); + minY = Math.min(y1, y2); + minZ = Math.min(z1, z2); + maxX = Math.max(x1, x2); + maxY = Math.max(y1, y2); + maxZ = Math.max(z1, z2); + } + + public boolean contains( + Location loc) { + if (loc == null) return false; + if (!loc.getWorld().getName() + .equals(world)) + return false; + int x = loc.getBlockX(); + int y = loc.getBlockY(); + int z = loc.getBlockZ(); + return x >= minX && x <= maxX + && y >= minY && y <= maxY + && z >= minZ && z <= maxZ; + } + } + + public PVPManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadRegions(); + } + + // ===== 独立数据库 ===== + + private void initDB() { + try { + Class.forName( + "org.sqlite.JDBC"); + File dbFile = new File( + plugin.getDataFolder(), + "pvp.db"); + pvpDb = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getAbsolutePath()); + Statement st = + pvpDb.createStatement(); + st.execute( + "PRAGMA journal_mode=WAL"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_regions (" + + "id INTEGER PRIMARY KEY " + + "AUTOINCREMENT," + + "name TEXT NOT NULL " + + "UNIQUE," + + "world TEXT NOT NULL," + + "x1 INTEGER NOT NULL," + + "y1 INTEGER NOT NULL," + + "z1 INTEGER NOT NULL," + + "x2 INTEGER NOT NULL," + + "y2 INTEGER NOT NULL," + + "z2 INTEGER NOT NULL," + + "create_time INTEGER " + + "DEFAULT 0)"); + st.execute( + "CREATE TABLE IF NOT EXISTS " + + "pvp_kills (" + + "player_name TEXT " + + "NOT NULL," + + "region_name TEXT " + + "NOT NULL," + + "kills INTEGER " + + "DEFAULT 0," + + "deaths INTEGER " + + "DEFAULT 0," + + "PRIMARY KEY" + + "(player_name, " + + "region_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException( + "[PVP] DB init failed: " + + e.getMessage(), e); + } + } + + // ===== DB 操作 ===== + + private void saveRegion(String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT OR REPLACE " + + "INTO pvp_regions" + + " (name, world," + + " x1,y1,z1," + + " x2,y2,z2," + + " create_time)" + + " VALUES " + + "(?,?,?,?,?,?,?,?,?)"); + ps.setString(1, name); + ps.setString(2, world); + ps.setInt(3, x1); + ps.setInt(4, y1); + ps.setInt(5, z1); + ps.setInt(6, x2); + ps.setInt(7, y2); + ps.setInt(8, z2); + ps.setLong(9, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Map + getRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT * FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + rs.close(); + ps.close(); + return row; + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + private List> + getAllRegions() { + List> list = + new ArrayList<>(); + try { + Statement st = + pvpDb.createStatement(); + ResultSet rs = + st.executeQuery( + "SELECT * FROM " + + "pvp_regions"); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + for (int i = 1; i <= rs + .getMetaData() + .getColumnCount(); i++) { + row.put(rs.getMetaData() + .getColumnName(i), + rs.getObject(i)); + } + list.add(row); + } + rs.close(); + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + private void deleteRegion(String name) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "DELETE FROM " + + "pvp_regions " + + "WHERE name=?"); + ps.setString(1, name); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private int getKills(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT kills FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("kills"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private int getDeaths(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT deaths FROM " + + "pvp_kills " + + "WHERE " + + "player_name=?" + + " AND " + + "region_name=?"); + ps.setString(1, player); + ps.setString(2, region); + ResultSet rs = + ps.executeQuery(); + int v = 0; + if (rs.next()) + v = rs.getInt("deaths"); + rs.close(); + ps.close(); + return v; + } catch (SQLException e) { + return 0; + } + } + + private void addKill(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,1,0) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET kills=" + + "kills+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void addDeath(String player, + String region) { + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "INSERT INTO pvp_kills" + + " (player_name," + + " region_name," + + " kills,deaths)" + + " VALUES " + + "(?,?,0,1) " + + "ON CONFLICT" + + "(player_name," + + " region_name)" + + " DO UPDATE " + + "SET deaths=" + + "deaths+1"); + ps.setString(1, player); + ps.setString(2, region); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private List> + getTop(String region, int limit) { + List> list = + new ArrayList<>(); + try { + PreparedStatement ps = + pvpDb.prepareStatement( + "SELECT player_name," + + " kills,deaths" + + " FROM " + + "pvp_kills " + + "WHERE " + + "region_name=?" + + " AND kills>0" + + " ORDER BY kills" + + " DESC LIMIT ?"); + ps.setString(1, region); + ps.setInt(2, limit); + ResultSet rs = + ps.executeQuery(); + while (rs.next()) { + Map row = + new LinkedHashMap<>(); + row.put("player_name", + rs.getString( + "player_name")); + row.put("kills", + rs.getInt("kills")); + row.put("deaths", + rs.getInt("deaths")); + list.add(row); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public Set getRegionNames() { + return regions.keySet(); + } + + // ===== 加载 ===== + + private void loadRegions() { + regions.clear(); + List> list = + getAllRegions(); + for (Map row : list) { + PVPRegion r = new PVPRegion(); + r.name = (String) row + .get("name"); + r.world = (String) row + .get("world"); + r.x1 = ((Number) row + .get("x1")).intValue(); + r.y1 = ((Number) row + .get("y1")).intValue(); + r.z1 = ((Number) row + .get("z1")).intValue(); + r.x2 = ((Number) row + .get("x2")).intValue(); + r.y2 = ((Number) row + .get("y2")).intValue(); + r.z2 = ((Number) row + .get("z2")).intValue(); + r.calcBounds(); + regions.put(r.name, r); + } + } + + // ===== 工具 ===== + + public ItemStack createTool() { + ItemStack tool = new ItemStack( + Material.BLAZE_ROD); + ItemMeta m = tool.getItemMeta(); + m.setDisplayName( + "§6§l[PVP] 区域选择工具"); + m.setLore(Arrays.asList( + "§7右键:设置第一个角", + "§7左键:设置第二个角")); + m.getPersistentDataContainer() + .set(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING, + "tool"); + tool.setItemMeta(m); + return tool; + } + + private boolean isPVPTool( + ItemStack item) { + if (item == null) return false; + ItemMeta m = item.getItemMeta(); + if (m == null) return false; + return m.getPersistentDataContainer() + .has(new NamespacedKey( + plugin, "pvp_tool"), + PersistentDataType.STRING); + } + + public void giveTool(Player p) { + boolean has = false; + for (ItemStack it : p.getInventory() + .getContents()) { + if (isPVPTool(it)) { + has = true; + break; + } + } + if (!has) { + p.getInventory() + .addItem(createTool()); + } + } + + // ===== 边框粒子 ===== + + private void drawBorder(Player target, + PVPRegion r) { + if (r == null) return; + World w = Bukkit.getWorld(r.world); + if (w == null) return; + int step = 2; + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.maxX, r.minY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.minY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.minX, r.minY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.minY, r.minZ, step); + drawLine(target, w, + r.minX, r.maxY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.minZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.maxX, r.maxY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.maxY, r.maxZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.minX, r.minY, r.minZ, + r.minX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.minZ, + r.maxX, r.maxY, r.minZ, step); + drawLine(target, w, + r.maxX, r.minY, r.maxZ, + r.maxX, r.maxY, r.maxZ, step); + drawLine(target, w, + r.minX, r.minY, r.maxZ, + r.minX, r.maxY, r.maxZ, step); + } + + private void drawLine(Player target, + World w, + int x1, int y1, int z1, + int x2, int y2, int z2, + int step) { + double dx = x2 - x1; + double dy = y2 - y1; + double dz = z2 - z1; + double dist = Math.sqrt( + dx * dx + dy * dy + dz * dz); + int steps = (int) (dist / step); + if (steps < 1) steps = 1; + for (int i = 0; i <= steps; i++) { + double t = (double) i / steps; + Location loc = new Location(w, + x1 + dx * t, + y1 + dy * t + 0.1, + z1 + dz * t); + w.spawnParticle( + Particle.FLAME, loc, 1, + 0.0, 0.0, 0.0, 0.0); + } + } + + // ===== 每秒 tick ===== + + public void tickRegions() { + long now = + System.currentTimeMillis(); + + // 玩家区域检测 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pn = p.getName(); + Location loc = p.getLocation(); + String inside = null; + for (PVPRegion r : + regions.values()) { + if (r.contains(loc)) { + inside = r.name; + break; + } + } + String was = inRegion.get(pn); + + if (inside != null && !inside.equals(was)) { + inRegion.put(pn, inside); + if (was == null) { + String reg = inside; + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> + updateScoreboard( + pp, reg), + 1L); + } + + } else if (inside == null + && was != null) { + inRegion.remove(pn); + // 不删 sessionKills,等倒计时结束再清 + removeScoreboard(p); + final Player pp = p; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> plugin + .giveMenuSnowball(pp), + 5L); + } + + } + + // 5分钟超时重置连杀session + for (String pname : + new ArrayList<>( + killSessions + .keySet())) { + KillSession ks = + killSessions.get(pname); + if (ks == null) continue; + if (now - ks.lastKill + > 5 * 60 * 1000) { + killSessions.remove(pname); + } + } + + // 5分钟无击杀 → 计数榜切总榜 + // 5分钟无击杀 → 归档并清零 + for (String rName : + new ArrayList<>( + lastKillTime + .keySet())) { + long last = lastKillTime + .getOrDefault(rName, 0L); + if (now - last > 5 * 60 * 1000) { + // 清零该区域所有sessionKills + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + rName + ":")); + // 切换到总榜 + refreshScoreboard(rName); + lastKillTime.remove(rName); + lastScoreboardRefresh.remove(rName); + + } + } + + + // 边框渲染 + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (pr == null) continue; + PVPRegion r = regions.get(pr); + if (r == null) continue; + if (permanentBorders + .contains(pr)) { + drawBorder(p, r); + } + if (tempBorders + .contains(pr)) { + drawBorder(p, r); + } + // 每秒刷新活跃区域的计分板倒计时 + for (String rName : new ArrayList<>(lastKillTime.keySet())) { + long last = lastScoreboardRefresh.getOrDefault(rName, 0L); + if (now - last >= 1000) { + lastScoreboardRefresh.put(rName, now); + for (Player tp : Bukkit.getOnlinePlayers()) { + String inR = inRegion.get(tp.getName()); + if (rName.equals(inR)) { + showCounter(tp, rName); + } + } + } + } + + + + } + } + + private void broadcastWithSound(String msg, Sound sound) { + Bukkit.broadcastMessage(msg); + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), sound, 1.0f, 1.0f); + } + } + + + private void initSession(String player, + String region) { + killSessions.remove(player); + tripleAnnounced.remove( + region + ":" + player); + KillSession s = new KillSession(); + s.region = region; + s.firstKill = + System.currentTimeMillis(); + s.lastKill = + System.currentTimeMillis(); + killSessions.put(player, s); + } + + // ===== 击杀事件 ===== + + @EventHandler + public void onDeath( + EntityDeathEvent event) { + if (!(event.getEntity() + instanceof Player)) return; + Player victim = + (Player) event.getEntity(); + Player killer = + victim.getKiller(); + if (killer == null) return; + if (killer.equals(victim)) return; + + String region = + inRegion.get( + victim.getName()); + if (region == null) return; + + String vName = victim.getName(); + String kName = killer.getName(); + + // 总榜 + addKill(kName, region); + addDeath(vName, region); + + // 计数榜 + String kKey = + region + ":" + kName; + sessionKills.merge(kKey, 1, + Integer::sum); + lastKillTime.put(region, + System.currentTimeMillis()); + + // 终结检测 + // 终结检测 + KillSession victimSession = + killSessions.get(vName); + if (victimSession != null + && victimSession.count >= 3) { + String endMsg = + "§b§l" + kName + + " §e终结了 " + + vName + " §e的 " + + victimSession.count + + "连杀!§a恭喜终结者 " + + kName + + "§e!§7快来挑战!"; + killSessions.remove(vName); + broadcastWithSound(endMsg, Sound.ENTITY_ENDER_DRAGON_GROWL); + + killSessions.remove(vName); + } + + + // 击杀者连杀 + KillSession ks = + killSessions.get(kName); + if (ks == null + || !ks.region.equals(region) + || System.currentTimeMillis() + - ks.lastKill > 5 * 60 * 1000) { + tripleAnnounced.remove( + region + ":" + kName); + KillSession ns = + new KillSession(); + ns.region = region; + ns.count = 1; + ns.firstKill = + System.currentTimeMillis(); + ns.lastKill = + System.currentTimeMillis(); + killSessions.put(kName, ns); + } else { + ks.count++; + ks.lastKill = + System.currentTimeMillis(); + } + + // 多杀通报 + KillSession killerSession = + killSessions.get(kName); + if (killerSession != null) { + plugin.getLogger().info( + "[PVP] checkMultiKill: " + + kName + " count=" + + killerSession.count + + " region=" + region); + checkMultiKill(killer, + region, killerSession); + } + + // 刷新计分板 + for (Player p : + Bukkit.getOnlinePlayers()) { + if (region.equals( + inRegion.get( + p.getName()))) { + updateScoreboard(p, region); + } + } + + victim.sendMessage("§c你在「" + + region + "」被 " + + kName + " 击杀"); + killer.sendMessage("§a你在「" + + region + "」击杀了 " + + vName); + } + + private void checkMultiKill( + Player killer, String region, + KillSession session) { + int count = session.count; + String kName = killer.getName(); + String key = region + ":" + kName; + String msg = null; + + if (count >= 5) { + msg = "§c§l" + kName + + " §e在「" + region + + "」达成 " + count + + "连杀!§7§l快来挑战!"; + } else if (count == 3) { + if (tripleAnnounced.add(key)) { + msg = "§6§l三杀!" + + kName + + " 势不可挡!" + + "§7快来挑战!"; + } + } + + if (msg != null) { + if (count >= 5) { + broadcastWithSound(msg, Sound.ENTITY_ENDER_DRAGON_GROWL); + } else { + broadcastWithSound(msg, Sound.BLOCK_END_PORTAL_SPAWN); + } + } + + } + + + + // ===== 计分板(英文ID) ===== + + private void ensureScoreboard( + Player p, String region) { + // 活跃击杀:每秒强制刷新(更新倒计时) + if (lastKillTime.containsKey(region)) { + showCounter(p, region); + return; + } + // 无活跃击杀:只在没有scoreboard时创建总榜 + Scoreboard sb = p.getScoreboard(); + if (sb == null + || sb.getObjective( + "sdf1pvp") == null) { + showTotal(p, region); + } + } + + + private void updateScoreboard( + Player p, String region) { + if (lastKillTime + .containsKey(region)) { + showCounter(p, region); + } else { + showTotal(p, region); + } + } + + private void showTotal(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = + mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§6§l击杀总榜 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List> top = + getTop(region, 10); + if (top.isEmpty()) { + obj.getScore("§7暂无记录") + .setScore(0); + } else { + int rank = 10; + for (Map row : + top) { + String name = (String) row + .get("player_name"); + int kills = ((Number) row + .get("kills")) + .intValue(); + int deaths = ((Number) row + .get("deaths")) + .intValue(); + String line = + "§e#" + (11 - rank) + + " §f" + name + + " §a" + kills + + "§7/§c" + deaths; + obj.getScore(line) + .setScore(rank); + rank--; + } + } + p.setScoreboard(sb); + } + private void showCounter(Player p, + String region) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + Scoreboard sb = mgr.getNewScoreboard(); + Objective obj = + sb.registerNewObjective( + "sdf1pvp", + Criteria.DUMMY, + "§c§l实时战况 [" + + region + "]"); + obj.setDisplaySlot( + DisplaySlot.SIDEBAR); + + List entries = + new ArrayList<>(); + for (Map.Entry e : + sessionKills.entrySet()) { + if (!e.getKey().startsWith( + region + ":")) + continue; + if (e.getValue() <= 0) continue; + String pName = e.getKey() + .substring( + region.length() + 1); + entries.add(new String[]{ + pName, + String.valueOf( + e.getValue())}); + } + + if (entries.isEmpty()) { + obj.getScore("§7等待击杀...") + .setScore(1); + } else { + entries.sort((a, b) -> + Integer.compare( + Integer.parseInt(b[1]), + Integer.parseInt(a[1]))); + int rank = 10; + for (String[] entry : entries) { + obj.getScore( + "§e" + entry[0] + + " §a" + entry[1]) + .setScore(rank); + rank--; + if (rank < 1) break; + } + } + + Long last = + lastKillTime.get(region); + if (last != null) { + long elapsed = + System.currentTimeMillis() + - last; + long remaining = + Math.max(0, + 5 * 60 * 1000 - elapsed); + long secs = remaining / 1000; + obj.getScore( + "§7冷却: " + secs + "s") + .setScore(-1); + } + + // 关键:强制赋值新scoreboard + p.setScoreboard(sb); + } + + private void removeScoreboard( + Player p) { + ScoreboardManager mgr = + Bukkit.getScoreboardManager(); + p.setScoreboard( + mgr.getNewScoreboard()); + } + + private void refreshScoreboard( + String regionName) { + for (Player p : + Bukkit.getOnlinePlayers()) { + String pr = + inRegion.get(p.getName()); + if (regionName.equals(pr)) { + showTotal(p, regionName); + } + } + } + + // ===== 工具选择事件 ===== + + @EventHandler + public void onInteract( + PlayerInteractEvent event) { + Player p = event.getPlayer(); + ItemStack hand = + p.getInventory() + .getItemInMainHand(); + if (!isPVPTool(hand)) return; + + if (!event.getAction() + .isRightClick() + && !event.getAction() + .isLeftClick()) return; + + Location loc = p.getLocation(); + String[] state = + selectState.get(p.getName()); + if (state == null) { + state = new String[]{ + loc.getWorld() + .getName(), + "", ""}; + selectState.put( + p.getName(), state); + } + + String coord = + loc.getBlockX() + "," + + loc.getBlockY() + "," + + loc.getBlockZ(); + + if (event.getAction() + .isRightClick()) { + state[0] = loc.getWorld() + .getName(); + state[1] = coord; + p.sendMessage( + "§a第一个角: " + coord); + } else { + state[2] = coord; + p.sendMessage( + "§a第二个角: " + coord); + } + event.setCancelled(true); + } + + // ===== 退出清理 ===== + + @EventHandler + public void onQuit( + PlayerQuitEvent event) { + String name = + event.getPlayer().getName(); + inRegion.remove(name); + selectState.remove(name); + } + + // ===== 指令 ===== + + public boolean onCommand(Player p, + String[] args) { + if (args.length == 0) { + return showStats(p, + p.getName()); + } + switch (args[0].toLowerCase()) { + case "create": + return handleCreate( + p, args); + case "stats": + if (args.length >= 2) + return showStats(p, + args[1]); + return showStats(p, + p.getName()); + case "list": + return handleList(p); + case "delete": + if (args.length >= 2) + return handleDelete(p, + args[1]); + p.sendMessage( + "§c用法: " + + "/pvp delete <名字>"); + return true; + case "tool": + giveTool(p); + p.sendMessage( + "§a已发放" + + "PVP区域选择工具"); + return true; + case "on": + return handleBorderOn( + p, args); + case "off": + return handleBorderOff( + p, args); + case "tempban": + return handleTempBan( + p, args); + default: + return showStats(p, + p.getName()); + } + } + + private boolean handleBorderOn( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp on <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + permanentBorders.add(name); + p.sendMessage("§a已开启「" + + name + "」永久边框显示"); + return true; + } + + private boolean handleBorderOff( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp off <区域名>"); + return true; + } + permanentBorders.remove(name); + tempBorders.remove(name); + p.sendMessage("§a已关闭「" + + name + "」边框显示"); + return true; + } + + private boolean handleTempBan( + Player p, String[] args) { + String name = args.length >= 2 + ? args[1] : null; + if (name == null) { + p.sendMessage("§c用法: " + + "/pvp tempban <区域名>"); + return true; + } + if (!regions + .containsKey(name)) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + tempBorders.add(name); + p.sendMessage("§a「" + name + + "」临时边框显示15秒"); + final String rName = name; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> + tempBorders + .remove(rName), + 300L); + return true; + } + + private boolean handleCreate( + Player p, String[] args) { + if (args.length >= 8) { + try { + int x1 = Integer.parseInt( + args[1]); + int y1 = Integer.parseInt( + args[2]); + int z1 = Integer.parseInt( + args[3]); + int x2 = Integer.parseInt( + args[4]); + int y2 = Integer.parseInt( + args[5]); + int z2 = Integer.parseInt( + args[6]); + String rName = args[7]; + return createRegion(p, + rName, + p.getWorld() + .getName(), + x1, y1, z1, + x2, y2, z2); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c坐标格式错误"); + return true; + } + } + + if (args.length >= 2) { + String[] state = + selectState + .get(p.getName()); + if (state == null + || state[1].isEmpty() + || state[2].isEmpty()) { + p.sendMessage( + "§c请先用工具选择两个角"); + return true; + } + String[] p1 = + state[1].split(","); + String[] p2 = + state[2].split(","); + return createRegion(p, + args[1], state[0], + Integer.parseInt(p1[0]), + Integer.parseInt(p1[1]), + Integer.parseInt(p1[2]), + Integer.parseInt(p2[0]), + Integer.parseInt(p2[1]), + Integer.parseInt(p2[2])); + } + + p.sendMessage( + "§c用法: /pvp create " + + " " + + " <名字>"); + p.sendMessage( + "§c或: /pvp create " + + "<名字> (先用工具选择)"); + return true; + } + + private boolean createRegion( + Player p, String name, + String world, + int x1, int y1, int z1, + int x2, int y2, int z2) { + if (getRegion(name) != null) { + p.sendMessage( + "§c区域已存在: " + name); + return true; + } + PVPRegion r = new PVPRegion(); + r.name = name; + r.world = world; + r.x1 = x1; + r.y1 = y1; + r.z1 = z1; + r.x2 = x2; + r.y2 = y2; + r.z2 = z2; + r.calcBounds(); + saveRegion(name, world, + x1, y1, z1, + x2, y2, z2); + regions.put(name, r); + p.sendMessage( + "§a§lPVP区域创建成功!"); + p.sendMessage( + "§7名称: §f" + name); + p.sendMessage("§7范围: §f" + + r.minX + "," + r.minY + + "," + r.minZ + " → " + + r.maxX + "," + r.maxY + + "," + r.maxZ); + selectState.remove(p.getName()); + return true; + } + + private boolean handleList( + Player p) { + List> all = + getAllRegions(); + if (all.isEmpty()) { + p.sendMessage( + "§7暂无PVP区域"); + return true; + } + p.sendMessage( + "§6§lPVP区域列表:"); + for (Map row : + all) { + p.sendMessage("§e- §f" + + row.get("name") + + " §7(" + row.get("world") + + ")"); + } + return true; + } + + private boolean handleDelete( + Player p, String name) { + if (getRegion(name) == null) { + p.sendMessage( + "§c区域不存在: " + name); + return true; + } + deleteRegion(name); + regions.remove(name); + permanentBorders.remove(name); + tempBorders.remove(name); + sessionKills.keySet() + .removeIf(k -> k + .startsWith( + name + ":")); + lastKillTime.remove(name); + lastScoreboardRefresh.remove(name); + p.sendMessage( + "§a已删除: " + name); + return true; + } + + private boolean showStats(Player p, + String target) { + String region = + inRegion.get(target); + if (region == null) { + p.sendMessage("§6§l" + target + + " 的PVP战绩:"); + List> all = + getAllRegions(); + boolean has = false; + for (Map row : + all) { + String rn = (String) row + .get("name"); + int kills = + getKills(target, rn); + int deaths = + getDeaths(target, rn); + if (kills > 0 + || deaths > 0) { + p.sendMessage( + "§e- §f" + rn + + " §a击杀:" + kills + + " §c死亡:" + deaths); + has = true; + } + } + if (!has) + p.sendMessage( + "§7暂无记录"); + } else { + int kills = + getKills(target, region); + int deaths = + getDeaths(target, region); + double kd = deaths > 0 + ? (double) kills / deaths + : kills; + p.sendMessage("§6§l" + target + + " @ " + region); + p.sendMessage( + "§a击杀: " + kills + + " §c死亡: " + deaths + + " §eKD: " + + String.format( + "%.2f", kd)); + } + return true; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/PasswordUtils.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PasswordUtils.java new file mode 100644 index 0000000..4029ed4 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PasswordUtils.java @@ -0,0 +1,72 @@ +package Sdf1_login; + +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Base64; + +public class PasswordUtils { + + private static final SecureRandom RANDOM = + new SecureRandom(); + + public static String generateSalt() { + byte[] salt = new byte[16]; + RANDOM.nextBytes(salt); + return Base64.getEncoder().encodeToString(salt); + } + + public static String hash(String password, String salt) { + try { + MessageDigest md = + MessageDigest.getInstance("SHA-256"); + md.update(Base64.getDecoder().decode(salt)); + byte[] hash = + md.digest(password.getBytes("UTF-8")); + return Base64.getEncoder().encodeToString(hash); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean validate(String password) { + if (password == null) return false; + if (password.length() < 6 || password.length() > 20) + return false; + boolean hasUpper = false; + boolean hasLower = false; + boolean hasDigit = false; + for (char c : password.toCharArray()) { + if (Character.isUpperCase(c)) hasUpper = true; + if (Character.isLowerCase(c)) hasLower = true; + if (Character.isDigit(c)) hasDigit = true; + } + return hasUpper && hasLower && hasDigit; + } + + public static String generateTempPassword() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + StringBuilder sb = new StringBuilder(8); + for (int i = 0; i < 8; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateInviteCode() { + String chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + StringBuilder sb = new StringBuilder(6); + for (int i = 0; i < 6; i++) { + sb.append(chars.charAt( + RANDOM.nextInt(chars.length()))); + } + return sb.toString(); + } + + public static String generateVerifyCode() { + int code = 100000 + RANDOM.nextInt(900000); + return String.valueOf(code); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/PlayerStorage.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PlayerStorage.java new file mode 100644 index 0000000..f93caa3 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PlayerStorage.java @@ -0,0 +1,130 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.*; +import java.util.logging.Logger; + +public class PlayerStorage { + private Connection conn; + private final Logger logger; + + public PlayerStorage(File dataFolder, + Logger logger) { + this.logger = logger; + File dbFile = new File( + dataFolder, + "quest_data.db"); + try { + Class.forName( + "org.sqlite.JDBC"); + conn = DriverManager + .getConnection( + "jdbc:sqlite:" + + dbFile + .getAbsolutePath()); + conn.setAutoCommit(true); + Statement s = + conn.createStatement(); + s.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_kv (" + + "player TEXT NOT NULL," + + "key TEXT NOT NULL," + + "value TEXT," + + "PRIMARY KEY" + + "(player, key))"); + s.close(); + logger.info( + "[QuestStorage] 初始化: " + + dbFile.getAbsolutePath()); + } catch (Exception e) { + logger.severe( + "[QuestStorage] 失败: " + + e.getMessage()); + } + } + + public Object getCondition( + String player, String key) { + if (conn == null) return null; + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value " + + "FROM quest_kv " + + "WHERE player=? " + + "AND key=?"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ResultSet rs = + ps.executeQuery(); + String val = rs.next() + ? rs.getString("value") + : null; + rs.close(); + ps.close(); + return parseValue(val); + } catch (SQLException e) { + return null; + } + } + + public void setCondition( + String player, String key) { + setConditionRaw( + player, key, "true"); + } + public void debugPlayer(String player) { + + } + public void setConditionRaw( + String player, String key, + String value) { + if (conn == null) return; + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO quest_kv" + + "(player,key,value)" + + " VALUES(?,?,?)"); + ps.setString(1, + player.toLowerCase()); + ps.setString(2, key); + ps.setString(3, value); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + logger.warning( + "[QuestStorage] 写入失败: " + + key + "=" + value + + " | " + e.getMessage()); + } + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) + conn.close(); + } catch (Exception e) {} + } + + private Object parseValue(String v) { + if (v == null) return null; + try { + return Long.parseLong(v); + } catch (Exception e1) { + try { + return Double.parseDouble(v); + } catch (Exception e2) { + if ("true".equalsIgnoreCase(v)) + return true; + if ("false".equalsIgnoreCase(v)) + return false; + return v; + } + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/PointsManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PointsManager.java new file mode 100644 index 0000000..77fc195 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/PointsManager.java @@ -0,0 +1,374 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + + +import java.lang.reflect.Method; +import java.util.*; + +public class PointsManager { + + private final Main plugin; + private Object cyInstance = null; + private Method cyActivationMethod = null; + private Method cyGetShopItemsMethod = null; + + private final List> cyItems = + new ArrayList<>(); + + public PointsManager(Main plugin) { + this.plugin = plugin; + discoverCY(); + } + + private void discoverCY() { + try { + org.bukkit.plugin.Plugin p = Bukkit + .getPluginManager() + .getPlugin("CY_beibao"); + if (p != null && p.isEnabled()) { + cyInstance = p; + cyGetShopItemsMethod = p.getClass() + .getMethod("onSdf1GetShopItems"); + cyActivationMethod = p.getClass() + .getMethod("onSdf1Activation", + String.class, + int.class, int.class); + plugin.getLogger().info( + "[Sdf1_login] CY_beibao 联控已连接"); + } + } catch (Exception ignored) { + } + } + + private void refreshCYItems() { + cyItems.clear(); + if (cyInstance == null + || cyGetShopItemsMethod == null) return; + try { + Object result = + cyGetShopItemsMethod.invoke(cyInstance); + if (result == null) return; + String data = result.toString(); + if (data.isEmpty()) return; + String[] items = data.split(";"); + for (String itemStr : items) { + String[] p = itemStr.split("\\|"); + if (p.length < 8) continue; + Map item = + new LinkedHashMap<>(); + item.put("id", p[0]); + item.put("name", p[1]); + item.put("days", + Integer.parseInt(p[2])); + item.put("slots", + Integer.parseInt(p[3])); + item.put("price", + Integer.parseInt(p[4])); + item.put("stock", + Integer.parseInt(p[5])); + item.put("icon", p[6]); + item.put("lifetime", + "1".equals(p[7])); + cyItems.add(item); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 拉取CY商品失败: " + + e.getMessage()); + } + } + + private void activateCY(String name, + int slots, int days) { + try { + if (cyInstance == null + || cyActivationMethod == null) return; + cyActivationMethod.invoke( + cyInstance, name, slots, days); + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] CY激活失败: " + + e.getMessage()); + } + } + + public int getPoints(String name) { + return ((Number) plugin.getDb() + .getField(name, "points")).intValue(); + } + + public Inventory createShopGUI(Player p) { + int points = getPoints(p.getName()); + Inventory inv = Bukkit.createInventory( + null, 54, + "§d§l积分商城 | 当前积分: " + points); + fillBg(inv); + inv.setItem(10, mkItem(Material.EMERALD, + "§a游戏币充值", + "§7消耗积分兑换游戏币", + "§7100积分 = $1000", + "", + "§e点击兑换")); + inv.setItem(12, mkItem(Material.ENDER_CHEST, + "§b背包空间", + "§7消耗积分兑换背包格子", + "§750积分 = 9格永久空间", + "", + "§e点击兑换")); + inv.setItem(14, mkItem(Material.EXPERIENCE_BOTTLE, + "§e经验瓶", + "§7消耗积分兑换经验", + "§720积分 = 10级经验", + "", + "§e点击兑换")); + if (cyInstance != null) { + inv.setItem(31, mkItem(Material.CHEST, + "§6背包商城", + "§7来自CY_beibao的商品", + "", + "§e点击查看")); + } + inv.setItem(49, mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public Inventory createCYShopGUI(Player p) { + refreshCYItems(); + int points = getPoints(p.getName()); + int size = Math.max(54, + ((cyItems.size() / 9) + 1) * 9); + if (size > 54) size = 54; + Inventory inv = Bukkit.createInventory( + null, size, + "§d§lCY背包商城 | 当前积分: " + points); + fillBg(inv); + for (int i = 0; i < cyItems.size() + && i < 45; i++) { + Map item = cyItems.get(i); + String name = (String) item.get("name"); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + List lore = new ArrayList<>(); + lore.add("§7价格: §e" + price + " 积分"); + if (lifetime) { + lore.add("§7格子: §e终身 +" + slots); + } else { + lore.add("§7格子: §e+" + slots); + lore.add("§7时间: §e" + days + "天"); + } + if (stock == 0) { + lore.add("§7库存: §a无限"); + } else if (stock == -1) { + lore.add("§7库存: §c已下架"); + } else { + lore.add("§7库存: §e" + stock); + } + lore.add(""); + if (points >= price && stock != -1) { + lore.add("§e点击购买"); + } else if (stock == -1) { + lore.add("§c已下架"); + } else { + lore.add("§c积分不足"); + } + inv.setItem(i, mkItem(Material.CHEST, + "§a" + name, + lore.toArray(new String[0]))); + } + inv.setItem(size - 1, + mkItem(Material.ARROW, "§7返回")); + return inv; + } + + public void addItem(String name, + int price, String cmd2) { + // 追加到商品配置文件 + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.io.FileWriter fw = + new java.io.FileWriter(file, true); + fw.write(name + ":" + price + + ":" + cmd2 + "\n"); + fw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void removeItem(int index) { + File file = new File( + plugin.getDataFolder(), + "ShopItems.txt"); + try { + java.util.List lines = + new java.util.ArrayList<>(); + java.io.BufferedReader br = + new java.io.BufferedReader( + new java.io.FileReader( + file)); + String line; + int i = 0; + while ((line = br.readLine()) != null) { + if (line.trim().isEmpty() + || line.startsWith("#")) { + lines.add(line); + continue; + } + if (i != index) { + lines.add(line); + } + i++; + } + br.close(); + java.io.PrintWriter pw = + new java.io.PrintWriter(file); + for (String l : lines) pw.println(l); + pw.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public void handleClick(Player p, int slot) { + int points = getPoints(p.getName()); + switch (slot) { + case 10: + if (points >= 100) { + plugin.getDb().deductPoints( + p.getName(), 100); + if (plugin.getEconomy() != null) { + plugin.getEconomy() + .depositPlayer(p, 1000); + } + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "100")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 12: + if (points >= 50) { + plugin.getDb().deductPoints( + p.getName(), 50); + activateCY(p.getName(), 9, 0); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "50")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 14: + if (points >= 20) { + plugin.getDb().deductPoints( + p.getName(), 20); + p.setLevel(p.getLevel() + 10); + p.sendMessage(plugin.getConfig2() + .msg("points_purchase_success") + .replace("{count}", "20")); + } else { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + } + break; + case 31: + if (cyInstance != null) { + p.openInventory( + createCYShopGUI(p)); + } + break; + } + } + + public void handleCYClick(Player p, int slot) { + if (slot < 0 || slot >= cyItems.size() + || slot >= 45) return; + Map item = cyItems.get(slot); + int price = ((Number) item.get("price")) + .intValue(); + int stock = ((Number) item.get("stock")) + .intValue(); + int days = ((Number) item.get("days")) + .intValue(); + int slots = ((Number) item.get("slots")) + .intValue(); + boolean lifetime = (boolean) item + .get("lifetime"); + + if (stock == -1) { + p.sendMessage("§c该商品已下架"); + return; + } + int points = getPoints(p.getName()); + if (points < price) { + p.sendMessage(plugin.getConfig2() + .msg("points_insufficient") + .replace("{points}", + String.valueOf(points))); + return; + } + + plugin.getDb().deductPoints(p.getName(), price); + int actDays = lifetime ? 0 : days; + activateCY(p.getName(), slots, actDays); + + String tl = lifetime + ? "终身" : (days + "天"); + p.sendMessage("§a购买成功!获得 " + + tl + " +" + slots + "格 背包空间"); + } + + private void fillBg(Inventory inv) { + ItemStack gl = mkItem( + Material.GRAY_STAINED_GLASS_PANE, " "); + for (int i = 0; i < inv.getSize(); i++) + inv.setItem(i, gl); + } + + private ItemStack mkItem(Material mat, String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestStorage.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestStorage.java new file mode 100644 index 0000000..45dbabf --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestStorage.java @@ -0,0 +1,138 @@ +package Sdf1_login; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +public class QuestStorage { + + private Connection conn; + + public QuestStorage(File dataFolder) { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File( + dataFolder, + "quest_data.db"); + conn = DriverManager.getConnection( + "jdbc:sqlite:" + + dbFile.getPath()); + initTable(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void initTable() + throws SQLException { + Statement stmt = + conn.createStatement(); + stmt.executeUpdate( + "CREATE TABLE IF NOT EXISTS " + + "quest_conditions (" + + "player TEXT NOT NULL, " + + "condition_key TEXT NOT NULL, " + + "value TEXT DEFAULT '1', " + + "timestamp INTEGER, " + + "PRIMARY KEY " + + "(player, condition_key))"); + stmt.close(); + } + + /** + * 标记条件已完成(value = "1") + */ + public void setCondition( + String player, + String condition) { + setConditionRaw( + player, condition, "1"); + } + + /** + * 写入任意值(计数器等) + */ + public void setConditionRaw( + String player, + String condition, + String value) { + try { + PreparedStatement ps = + conn.prepareStatement( + "INSERT OR REPLACE " + + "INTO " + + "quest_conditions " + + "(player, " + + "condition_key, " + + "value, timestamp) " + + "VALUES " + + "(?, ?, ?, ?)"); + ps.setString(1, player); + ps.setString(2, condition); + ps.setString(3, value); + ps.setLong(4, + System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + /** + * 查询条件值 + */ + public Object getCondition( + String player, + String condition) { + try { + PreparedStatement ps = + conn.prepareStatement( + "SELECT value FROM " + + "quest_conditions " + + "WHERE player = ? " + + "AND " + + "condition_key = ?"); + ps.setString(1, player); + ps.setString(2, condition); + ResultSet rs = + ps.executeQuery(); + if (rs.next()) { + String val = + rs.getString("value"); + rs.close(); + ps.close(); + // 尝试解析为数字 + try { + if (val.contains(".")) { + return Double.parseDouble( + val); + } + return Long.parseLong(val); + } catch (NumberFormatException e) { + return val; + } + } + rs.close(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public void close() { + try { + if (conn != null + && !conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestTracker.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestTracker.java new file mode 100644 index 0000000..c147723 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/QuestTracker.java @@ -0,0 +1,3537 @@ +package Sdf1_login; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.generator.structure.Structure; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; +// 如果 Bonds 在 Sdf1_login 包下: +import Sdf1_login.BondManager; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + + +public class QuestTracker { + + private final Plugin plugin; + private final QuestStorage storage; + private final Map + playerBiome = new HashMap<>(); + private final Map> + questCache = new HashMap<>(); + private final List + checkInRecords = new ArrayList<>(); + private final Map + lastCheckInPos = new HashMap<>(); + + public void recordCheckIn( + String player, + String biomeKey, + String category, + String worldName, + double x, double y, double z) { + String record = player + + "|" + worldName + + "|" + biomeKey + + "|" + category + + "|" + String.format( + "%.1f", x) + + "|" + String.format( + "%.1f", y) + + "|" + String.format( + "%.1f", z) + + "|" + System.currentTimeMillis(); + checkInRecords.add(record); + plugin.getLogger().info( + "[QuestTracker] 打卡记录: " + + record); + } + + public List getCheckInRecords() { + return new ArrayList<>(checkInRecords); + } + + public List getPlayerRecords( + String player) { + List list = new ArrayList<>(); + for (String r : checkInRecords) { + if (r.startsWith( + player.toLowerCase() + + "|")) { + list.add(r); + } + } + return list; + } + + + public QuestTracker(Plugin plugin) { + this.plugin = plugin; + this.storage = new QuestStorage( + plugin.getDataFolder()); + + loadAllQuests(); + } + + public void debugPlayer(String player) { + plugin.getLogger().info( + "[QuestTracker] === " + + player + " 任务数据 ==="); + } + public void shutdown() { + if (storage != null) { + storage.close(); + } + } + + /** + * 玩家移动时记录当前群系 + */ + public void onPlayerMove( + String player, + String biomeName) { + playerBiome.put(player, biomeName); + } + /** + * 标记玩家已打卡该群系 + */ + // ========== 群系分类映射表 ========== + private static final Map + BIOME_CATEGORY = new HashMap<>(); + static { + // 海洋群系 + BIOME_CATEGORY.put("minecraft:ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_warm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_lukewarm_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_cold_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:frozen_ocean", "海洋"); + BIOME_CATEGORY.put("minecraft:deep_frozen_ocean", "海洋"); + // 平原群系 + BIOME_CATEGORY.put("minecraft:plains", "平原"); + BIOME_CATEGORY.put("minecraft:sunflower_plains", "平原"); + BIOME_CATEGORY.put("minecraft:meadow", "平原"); + // 森林群系 + BIOME_CATEGORY.put("minecraft:forest", "森林"); + BIOME_CATEGORY.put("minecraft:flower_forest", "森林"); + BIOME_CATEGORY.put("minecraft:birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_birch_forest", "森林"); + BIOME_CATEGORY.put("minecraft:dark_forest", "森林"); + BIOME_CATEGORY.put("minecraft:taiga", "森林"); + BIOME_CATEGORY.put("minecraft:snowy_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_pine_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:old_growth_spruce_taiga", "森林"); + BIOME_CATEGORY.put("minecraft:cherry_grove", "森林"); + BIOME_CATEGORY.put("minecraft:jungle", "森林"); + BIOME_CATEGORY.put("minecraft:sparse_jungle", "森林"); + BIOME_CATEGORY.put("minecraft:bamboo_jungle", "森林"); + // 沙漠群系 + BIOME_CATEGORY.put("minecraft:desert", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna", "沙漠"); + BIOME_CATEGORY.put("minecraft:savanna_plateau", "沙漠"); + BIOME_CATEGORY.put("minecraft:beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:snowy_beach", "沙漠"); + BIOME_CATEGORY.put("minecraft:windswept_savanna", "沙漠"); + // 恶地群系 + BIOME_CATEGORY.put("minecraft:badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:wooded_badlands", "恶地"); + BIOME_CATEGORY.put("minecraft:eroded_badlands", "恶地"); + // 雪原群系 + BIOME_CATEGORY.put("minecraft:snowy_plains", "雪原"); + BIOME_CATEGORY.put("minecraft:grove", "雪原"); + BIOME_CATEGORY.put("minecraft:ice_spikes", "雪原"); + BIOME_CATEGORY.put("minecraft:jagged_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_river", "雪原"); + BIOME_CATEGORY.put("minecraft:frozen_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:stony_peaks", "雪原"); + BIOME_CATEGORY.put("minecraft:snowy_slopes", "雪原"); + // 沙砾群系 + BIOME_CATEGORY.put("minecraft:windswept_hills", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_forest", "沙砾"); + BIOME_CATEGORY.put("minecraft:windswept_gravelly_hills", "沙砾"); + // 沼泽群系 + BIOME_CATEGORY.put("minecraft:swamp", "沼泽"); + BIOME_CATEGORY.put("minecraft:mangrove_swamp", "沼泽"); + // 河流 + BIOME_CATEGORY.put("minecraft:river", "河流"); + // 蘑菇岛 + BIOME_CATEGORY.put("minecraft:mushroom_fields", "蘑菇岛"); + // 地下群系 + BIOME_CATEGORY.put("minecraft:dripstone_caves", "地下"); + BIOME_CATEGORY.put("minecraft:lush_caves", "地下"); + BIOME_CATEGORY.put("minecraft:deep_dark", "地下"); + // 下界群系 + BIOME_CATEGORY.put("minecraft:nether_wastes", "下界"); + BIOME_CATEGORY.put("minecraft:crimson_forest", "下界"); + BIOME_CATEGORY.put("minecraft:warped_forest", "下界"); + BIOME_CATEGORY.put("minecraft:soul_sand_valley", "下界"); + BIOME_CATEGORY.put("minecraft:basalt_deltas", "下界"); + // 岩岸/沙滩 + BIOME_CATEGORY.put("minecraft:stony_shore", + "石岸"); +// 海岸 + BIOME_CATEGORY.put("minecraft:mushroom_fields", + "蘑菇岛"); +// 苍白之园 + BIOME_CATEGORY.put( + "minecraft:pale_garden", "森林"); +// 末地小型岛屿 + BIOME_CATEGORY.put( + "minecraft:small_end_islands", "末地"); +// 主世界结构归类 + BIOME_CATEGORY.put("minecraft:village", "平原"); + BIOME_CATEGORY.put( + "minecraft:desert_pyramid", "沙漠"); + BIOME_CATEGORY.put( + "minecraft:jungle_pyramid", "森林"); + BIOME_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + + // 末地群系 + BIOME_CATEGORY.put("minecraft:the_end", "末地"); + BIOME_CATEGORY.put("minecraft:the_void", "末地"); + BIOME_CATEGORY.put("minecraft:end_highlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_midlands", "末地"); + BIOME_CATEGORY.put("minecraft:end_barrens", "末地"); + + + } + + // 所有群系大类列表 + private static final String[] + ALL_CATEGORIES = { + "海洋", "平原", "森林", "沙漠", + "恶地", "雪原", "沙砾", "沼泽", + "河流", "石岸", "蘑菇岛", "地下", + "下界", "末地" + }; + + // ===== 结构映射(PaperMC 1.21.4 实际结构 key)===== + private static final Map + STRUCTURE_CATEGORY = + new HashMap<>(); + static { + // 主世界结构 + STRUCTURE_CATEGORY.put( + "minecraft:village_plains", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_desert", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_savanna", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_snowy", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_taiga", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:desert_pyramid", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:jungle_pyramid", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:swamp_hut", "沼泽"); + STRUCTURE_CATEGORY.put( + "minecraft:igloo", "雪原"); + STRUCTURE_CATEGORY.put( + "minecraft:mansion", "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:pillager_outpost", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:mineshaft_mesa", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:ancient_city", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:trail_ruins", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:trial_chambers", + "地下"); + STRUCTURE_CATEGORY.put( + "minecraft:monument", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_cold", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_warm", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck_beached", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:buried_treasure", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal", + "平原"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_desert", + "沙漠"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_jungle", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_mountain", + "山地"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_ocean", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_swamp", + "沼泽"); + // 下界结构 + STRUCTURE_CATEGORY.put( + "minecraft:fortress", "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:bastion_remnant", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:nether_fossil", + "下界"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_nether", + "下界"); + // 末地结构 + STRUCTURE_CATEGORY.put( + "minecraft:end_city", "末地"); + } + + // 所有群系分类的条件关键词映射 + private static final Map + CATEGORY_KEYWORDS = new HashMap<>(); + static { + CATEGORY_KEYWORDS.put("海洋", "海洋"); + CATEGORY_KEYWORDS.put("平原", "平原"); + CATEGORY_KEYWORDS.put("森林", "森林"); + CATEGORY_KEYWORDS.put("沙漠", "沙漠"); + CATEGORY_KEYWORDS.put("恶地", "恶地"); + CATEGORY_KEYWORDS.put("雪原", "雪原"); + CATEGORY_KEYWORDS.put("沙砾", "沙砾"); + CATEGORY_KEYWORDS.put("沼泽", "沼泽"); + CATEGORY_KEYWORDS.put("河流", "河流"); + CATEGORY_KEYWORDS.put("蘑菇岛", "蘑菇岛"); + CATEGORY_KEYWORDS.put("地下", "地下"); + CATEGORY_KEYWORDS.put("下界", "下界"); + CATEGORY_KEYWORDS.put("末地", "末地"); + // 条件文本中的别名映射 + CATEGORY_KEYWORDS.put("地狱", "下界"); + CATEGORY_KEYWORDS.put("下届", "下界"); + CATEGORY_KEYWORDS.put("主岛", "末地"); + CATEGORY_KEYWORDS.put("石岸", "石岸"); + CATEGORY_KEYWORDS.put("沙滩", "石岸"); + CATEGORY_KEYWORDS.put("海岸", "石岸"); + + // ===== 结构名称关键词映射 ===== + // 主世界结构 + CATEGORY_KEYWORDS.put("村庄", "村庄"); + CATEGORY_KEYWORDS.put("沙漠神殿", "沙漠"); + CATEGORY_KEYWORDS.put("丛林神庙", "森林"); + CATEGORY_KEYWORDS.put("沼泽小屋", "沼泽"); + CATEGORY_KEYWORDS.put("雪屋", "雪原"); + CATEGORY_KEYWORDS.put("林地府邸", "森林"); + CATEGORY_KEYWORDS.put("掠夺者前哨站", "平原"); + CATEGORY_KEYWORDS.put("废弃矿井", "地下"); + CATEGORY_KEYWORDS.put("要塞", "地下"); + CATEGORY_KEYWORDS.put("远古城市", "地下"); + CATEGORY_KEYWORDS.put("古迹废墟", "地下"); + CATEGORY_KEYWORDS.put("试炼密室", "地下"); + CATEGORY_KEYWORDS.put("海底废墟", "海洋"); + CATEGORY_KEYWORDS.put("沉船", "海洋"); + CATEGORY_KEYWORDS.put("埋藏的宝箱", "海洋"); + CATEGORY_KEYWORDS.put("废弃传送门", "主世界"); + CATEGORY_KEYWORDS.put("化石", "主世界"); + + // 下界结构 + CATEGORY_KEYWORDS.put("下界要塞", "下界"); + CATEGORY_KEYWORDS.put("猪灵堡垒", "下界"); + CATEGORY_KEYWORDS.put("玄武岩结构", "下界"); + + // 末地结构 + CATEGORY_KEYWORDS.put("末地城", "末地"); + CATEGORY_KEYWORDS.put("末地船", "末地"); + + // 下界相关别名 + CATEGORY_KEYWORDS.put("地狱要塞", "下界"); + CATEGORY_KEYWORDS.put("下界堡垒", "下界"); + + } + + /** + * 用世界维度名分类(最可靠) + * 世界名含 the_nether → 下界 + * 世界名含 the_end → 末地 + * 其余 → 主世界 + */ + public String mapBiomeCategory( + String rawKey, String worldName) { + // 优先用世界名判断维度 + if (worldName != null) { + String w = + worldName.toLowerCase(); + if (w.contains("nether")) + return "下界"; + if (w.contains("end")) + return "末地"; + } + + // 用群系映射表 + if (rawKey != null) { + String lower = + rawKey.toLowerCase(); + // 带 minecraft: 前缀的直接查 + String cat = + BIOME_CATEGORY.get(lower); + if (cat != null) + return cat; + + // 不带前缀的尝试补全 + String prefixed = + "minecraft:" + lower; + cat = BIOME_CATEGORY.get(prefixed); + if (cat != null) + return cat; + + // 模糊匹配群系名 + for (Map.Entry entry + : BIOME_CATEGORY.entrySet()) { + if (entry.getKey() + .contains(lower) + || lower.contains( + entry.getKey() + .replace( + "minecraft:", + ""))) { + return entry.getValue(); + } + } + } + + return "主世界"; + } + + // ========== 结构检测 ========== + + /** + * 检测指定位置是否存在自然生成的结构 + * 返回结构分类(如"村庄"、"沙漠"等),未找到返回null + * 使用 PaperMC World.hasStructureAt() + Registry.STRUCTURE API + */ + private String detectStructureAt( + String worldName, double x, + double y, double z) { + try { + World w = Bukkit.getWorld(worldName); + if (w == null) return null; + + // 使用 BlockPosition 提高性能 + io.papermc.paper.math.Position pos = + io.papermc.paper.math.Position.block( + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + + // 遍历所有自然生成的结构 + for (Structure structure : Registry.STRUCTURE) { + if (w.hasStructureAt(pos, structure)) { + // 获取结构 NamespacedKey(如 minecraft:village) + NamespacedKey structureKey = Registry.STRUCTURE.getKey(structure); + String keyStr = (structureKey != null) ? structureKey.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY.get(keyStr); + if (cat != null) { + plugin.getLogger().info( + "[QuestTracker] 检测到结构: " + + keyStr + + " -> " + cat); + return cat; + } + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测失败: " + + ex.getMessage()); + } + return null; + } + + /** + * 将结构中文名映射到对应的大类 + * 用于匹配任务条件中的结构名 + */ + private String mapStructureCategory( + String structureName) { + if (structureName == null) return ""; + + // 主世界结构 + if (structureName.contains("村庄")) + return "村庄"; + if (structureName.contains("沙漠神殿")) + return "沙漠"; + if (structureName.contains("丛林神庙")) + return "森林"; + if (structureName.contains("沼泽小屋")) + return "沼泽"; + if (structureName.contains("雪屋")) + return "雪原"; + if (structureName.contains("林地府邸")) + return "森林"; + if (structureName.contains("掠夺者前哨站")) + return "平原"; + if (structureName.contains("废弃矿井")) + return "地下"; + if (structureName.contains("要塞")) + return "地下"; + if (structureName.contains("远古城市")) + return "地下"; + if (structureName.contains("古迹废墟")) + return "地下"; + if (structureName.contains("试炼密室")) + return "地下"; + if (structureName.contains("海底废墟")) + return "海洋"; + if (structureName.contains("沉船")) + return "海洋"; + if (structureName.contains("埋藏的宝箱")) + return "海洋"; + if (structureName.contains("废弃传送门")) + return "主世界"; + if (structureName.contains("化石")) + return "主世界"; + + // 下界结构 + if (structureName.contains("下界要塞")) + return "下界"; + if (structureName.contains("猪灵堡垒")) + return "下界"; + if (structureName.contains("玄武岩结构")) + return "下界"; + + // 末地结构 + if (structureName.contains("末地城")) + return "末地"; + if (structureName.contains("末地船")) + return "末地"; + + return ""; + } + + +// ========== 解析为区块 ========== + + private List
    parseIntoSections( + List lines) { + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + return parseHashSections(lines); + } else if (hasDash) { + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + continue; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseHashSections(converted); + } + return new ArrayList<>(); + } + + private List
    parseHashSections( + List lines) { + List
    sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add(new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } else if (t.isEmpty()) { + // skip + } else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty() + && curTitle != null) { + curLines.add(cond); + } + } + } + if (curTitle != null) { + sections.add(new Section( + curTitle, curLines)); + } + return sections; + } + + private List
    buildFlatSections( + List lines, + String category) { + List conditions = + new ArrayList<>(); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + conditions.add(cond); + } + } + if (conditions.isEmpty()) + return new ArrayList<>(); + List
    list = + new ArrayList<>(); + list.add(new Section( + "第一阶段", conditions)); + return list; + } + + // ========== 文件加载入口 ========== + + private void loadAllQuests() { + loadCategory("新人任务"); + loadCategory("主线任务"); + loadCategory("支线任务"); + } + + private void loadCategory(String name) { + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 扫描中..."); + File folder = new File( + plugin.getDataFolder(), + name); + String path = "plugins/Sdf1_login/" + + name + "/"; + + if (!folder.exists()) { + folder.mkdirs(); + plugin.getLogger().warning( + "[QuestTracker] 已创建目录: " + + path); + questCache.put(name, + new ArrayList<>()); + return; + } + + File[] mdFiles = folder.listFiles( + (d, n) -> n.endsWith(".md")); + + if (mdFiles == null + || mdFiles.length == 0) { + plugin.getLogger().warning( + "[QuestTracker] " + path + + " 下无 .md 文件"); + questCache.put(name, + new ArrayList<>()); + return; + } + Arrays.sort(mdFiles); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 发现 " + mdFiles.length + + " 个文件,开始扫描..."); + + // 合并所有文件的阶段 + TreeMap merged = + new TreeMap<>(); + int fileCount = 0; + + for (int fi = 0; + fi < mdFiles.length; fi++) { + File file = mdFiles[fi]; + List lines = + readFileLines(file); + + if (lines.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 空文件,跳过"); + continue; + } + + List
    sections = + parseIntoSections(lines); + + if (sections.isEmpty()) { + sections = buildFlatSections( + lines, name); + } + + if (sections.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + + file.getName() + + ": 无法识别,跳过"); + continue; + } + + // 每个文件独立处理阶段号 + int defaultNum = fi + 1; + buildSingleFileStages( + sections, name, + merged, defaultNum); + + fileCount++; + } + + if (merged.isEmpty()) { + plugin.getLogger().warning( + "[QuestTracker] " + name + + ": 无有效任务数据"); + questCache.put(name, + new ArrayList<>()); + return; + } + + // 日志 + for (Map.Entry en + : merged.entrySet()) { + int num = en.getKey() >= 1000 + ? en.getKey() - 999 + : en.getKey(); + plugin.getLogger().info( + "[QuestTracker] " + name + + " 阶段" + num + + " \"" + + en.getValue().displayName + + "\" → 条件" + + en.getValue() + .conditions.size() + + "项, 详情" + + en.getValue() + .details.size() + + "项, 奖励" + + en.getValue() + .rewards.size() + + "项"); + } + + List list = + new ArrayList<>( + merged.values()); + questCache.put(name, list); + + plugin.getLogger().info( + "[QuestTracker] " + name + + ": 解析完成 → " + + list.size() + " 个阶段" + + " (来自 " + fileCount + + " 个文件)"); + } + + private Integer extractStageNumber( + String title) { + if (title == null) return null; + String t = title.toLowerCase().trim(); + + // 含阶段/stage/关/chapter 关键词 + boolean isStage = + t.contains("阶段") + || t.contains("stage") + || t.contains("关卡") + || t.contains("章节") + || t.contains("chapter"); + + if (isStage) { + String digits = title + .replaceAll("[^0-9]", ""); + if (!digits.isEmpty()) { + try { + return Integer.parseInt(digits); + } catch (NumberFormatException e) { + // fall through + } + } + return parseChineseNumber(title); + } + + // 无关键词但以数字开头(如 "1. 挖掘") + String trimmed = t + .replaceAll("^[.、\\s]+", "") + .trim(); + if (trimmed.length() > 0 + && Character.isDigit( + trimmed.charAt(0))) { + String num = trimmed + .replaceAll("[^0-9]", ""); + if (!num.isEmpty()) { + try { + return Integer.parseInt(num); + } catch (NumberFormatException e) { + // fall through + } + } + } + + return null; + } +// ========== 领取记录 ========== + + /** + * 标记已领取某阶段奖励 + */ + public void markClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return; + // 用阶段编号代替显示名,保证稳定 + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + storage.setCondition(player, key); + } + + public boolean hasClaimed( + String player, QuestFile qf) { + if (player == null || qf == null) + return false; + int num = getStageNumber(qf); + String key = "claimed_" + + qf.fileName + "_" + num; + return toBool( + storage.getCondition( + player, key)); + } + + private int parseChineseNumber( + String title) { + String[] cn = {"一", "二", "三", + "四", "五", "六", "七", + "八", "九", "十"}; + if (title.contains("十")) { + for (int j = 0; j < 9; j++) { + if (title.contains(cn[j])) { + return 10 + j + 1; + } + } + return 10; + } + for (int i = 0; i < 9; i++) { + if (title.contains(cn[i])) { + return i + 1; + } + } + return 0; + } + + // ========== md 解析核心 ========== + + /** + * 读取文件所有行 + */ + private List readFileLines( + File file) { + List lines = + new ArrayList<>(); + try { + BufferedReader br = + new BufferedReader( + new FileReader(file)); + String line; + while ((line = br.readLine()) + != null) { + lines.add(line); + } + br.close(); + } catch (Exception e) { + plugin.getLogger().warning( + "[QuestTracker] 读取 " + + file.getName() + + " 失败: " + + e.getMessage()); + } + return lines; + } + + /** + * 自动检测格式并解析单个文件 + */ + private List parseMdFile( + File file, String category) { + List lines = + readFileLines(file); + if (lines.isEmpty()) + return new ArrayList<>(); + + // 检测分隔符 + boolean hasHash = false; + boolean hasDash = false; + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("#")) + hasHash = true; + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) + hasDash = true; + } + + if (hasHash) { + // # 分隔格式 + return parseSections( + lines, category); + } else if (hasDash) { + // ---- 分隔格式:转换为 # 后复用 + List converted = + new ArrayList<>(); + for (String l : lines) { + String t = l.trim(); + if (t.startsWith("----") + || t.startsWith("====") + || t.startsWith("****")) { + String title = t + .replaceAll( + "^[-=*]+\\s*", + "") + .replaceAll( + "\\s*[-=*]+$", + "") + .trim(); + if (title.isEmpty()) + title = "separator"; + converted.add("# " + title); + } else { + converted.add(l); + } + } + return parseSections( + converted, category); + } else { + // 无分隔符:整文件一个阶段 + return parseFlat(lines, category); + } + } + + /** + * # 分隔格式 → 区块 → 阶段 + */ + private List parseSections( + List lines, + String category) { + // 拆分为区块 + List
    sections = + new ArrayList<>(); + String curTitle = null; + List curLines = + new ArrayList<>(); + + for (String raw : lines) { + String t = raw.trim(); + + // # 开头 → 新区块 + if (t.startsWith("#")) { + if (curTitle != null) { + sections.add( + new Section( + curTitle, + new ArrayList<>( + curLines))); + } + curTitle = t.replaceFirst( + "^#+\\s*", "").trim(); + curLines = new ArrayList<>(); + } + // 空行/分隔线 → 跳过 + else if (t.isEmpty() + || t.startsWith("----") + || t.startsWith("====") + || t.startsWith("***")) { + // skip + } + // 普通行 → 区块内容 + else { + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + curLines.add(cond); + } + } + } + // 保存最后一个区块 + if (curTitle != null) { + sections.add( + new Section( + curTitle, curLines)); + } + + // 直接转为 QuestFile 列表 + List result = + new ArrayList<>(); + int num = 1; + for (Section sec : sections) { + QuestFile qf = new QuestFile(); + qf.displayName = + extractDisplayName( + sec.title); + qf.fileName = category; + qf.conditions = + new ArrayList<>(sec.lines); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + result.add(qf); + num++; + } + return result; + } + + /** + * 无分隔符:整文件 = 一个阶段 + */ + private List parseFlat( + List lines, + String category) { + List list = + new ArrayList<>(); + QuestFile qf = makeDefaultStage( + category); + for (String raw : lines) { + String t = raw.trim(); + if (t.isEmpty()) continue; + String cond = t.replaceFirst( + "^-\\s+", "").trim(); + if (!cond.isEmpty()) { + qf.conditions.add(cond); + } + } + if (!qf.conditions.isEmpty()) { + list.add(qf); + } + return list; + } +// ========== Vault 经济奖励发放 ========== + + /** + * 发放阶段奖励 + * 含经济内容的走 Vault API或债券 + */ + public void claimRewards( + Player player, + QuestFile qf) { + if (qf == null + || qf.rewards == null + || qf.rewards.isEmpty()) { + player.sendMessage("§c无可领取的奖励"); + return; + } + + Main main = (Main) plugin; + // ★ 读取配置:economy = Vault,bonds = 债券(默认) + boolean useEconomy = "economy".equalsIgnoreCase( + main.getConfigMgr().rewardChannel); + + // Vault 经济引用(仅 useEconomy 时使用) + net.milkbowl.vault.economy.Economy econ = null; + if (useEconomy) { + econ = main.getEconomy(); + } + + int sentCount = 0; + + for (String reward : qf.rewards) { + String rt = reward.trim(); + if (rt.isEmpty()) continue; + + // 检查可选奖励标记 + if (hasOptionalMarker(rt)) { + if (!areAllConditionsMet(player.getName(), qf)) { + player.sendMessage( + "§7[可选奖励] 需完成所有可选条件: " + rt); + continue; + } + } + + // 按 + 分割子项 + String[] parts = rt.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 尝试识别经济金额 + double amt = extractEconomyAmount(part); + if (amt > 0) { + // ★ 根据配置分发 + if (useEconomy && econ != null) { + // —— Vault 经济 —— + double before = + econ.getBalance(player); + econ.depositPlayer(player, amt); + double after = + econ.getBalance(player); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(余额: §e" + + String.format("%.2f", before) + + " §7→ §a" + + String.format("%.2f", after) + + "§7)"); + if (plugin instanceof Main) { + ((Main) plugin).getCommission() + .payCommission( + player.getName(), amt); + } + } else { + // —— 债券(默认) —— + int bondAmt = + (int) Math.round(amt); + BondManager bonds = main.getBonds(); + if (bonds != null) { + int before = bonds.getBonds( + player.getName()); + bonds.addBonds( + player.getName(), + bondAmt, + "quest_reward", + qf.displayName, + "任务系统", + "任务奖励:" + qf.displayName); + int after = bonds.getBonds( + player.getName()); + player.sendMessage( + "§a§l[任务奖励] §f" + part + + " §7(债券: §e" + + before + + " §7→ §a" + + after + "§7)"); + } else { + player.sendMessage( + "§c债券系统不可用"); + } + } + sentCount++; + continue; + } + + // 尝试发物品 + java.util.List items = + parseRewardItems(part); + if (!items.isEmpty()) { + for (ItemStack is : items) { + player.getInventory().addItem(is); + } + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + continue; + } + + // 非经济非物品 → 仅消息 + player.sendMessage( + "§a§l[任务奖励] §f" + part); + sentCount++; + } + } + + plugin.getLogger().info( + "[QuestTracker] " + player.getName() + + " 预取了 \"" + qf.displayName + + "\" 共 " + sentCount + "项奖励"); + } + + /** + * 检查奖励文本是否含可选标记 + */ + private boolean hasOptionalMarker( + String text) { + return text.contains("(可选)") + || text.contains("(进阶)") + || text.contains("【可选】") + || text.contains("【进阶】"); + } + + /** + * 检查阶段所有条件(含可选)是否全部满足 + * 用于决定可选奖励是否发放 + */ + private boolean areAllConditionsMet( + String player, QuestFile qf) { + if (qf.conditions == null + || qf.conditions.isEmpty()) + return true; + for (String cond : qf.conditions) { + if (!isConditionMet(player, cond)) + return false; + } + return true; + } + + /** + * 从奖励文本中提取经济金额 + * 支持: "500游戏币" "500" "金币500" + * "$500" "1000-5000"(随机) "1000~5000" + */ + private double extractEconomyAmount( + String text) { + String t = text.replaceAll(" ", ""); + + // 必须包含经济关键词才算经济奖励 + boolean hasEcon = + t.contains("游戏币") + || t.contains("金币") + || t.contains("积分") + || t.contains("块钱") + || t.contains("元") + || t.contains("$") + || t.contains("经济") + || t.contains("余额") + || t.contains("发钱") + || t.contains("债券"); // ★ 新增 + + + if (!hasEcon) return 0; + + // 范围: "1000-5000" 或 "1000~5000" + Matcher rm = Pattern + .compile("(\\d+)[-~](\\d+)") + .matcher(t); + if (rm.find()) { + double min = Double.parseDouble( + rm.group(1)); + double max = Double.parseDouble( + rm.group(2)); + return min + Math.random() + * (max - min); + } + + // 纯数字(含 $ 前缀) + Matcher nm = Pattern + .compile("\\$?(\\d+(\\.\\d+)?)") + .matcher(t); + if (nm.find()) { + return Double.parseDouble( + nm.group(1)); + } + + return 0; + } + /** + * 解析奖励物品文本 + * "皮革套装*1" → 4件皮革装备 + * "木剑*1" → 1把木剑 + * "钻石*64" → 64颗钻石 + */ + private List parseRewardItems( + String text) { + List result = + new ArrayList<>(); + + // 去掉条件前缀 + // 如 "完成注册登录:皮革套装*1" + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + if (text.contains(":")) { + text = text.substring( + text.indexOf(":") + 1); + } + + // 按 + 分割多个物品 + String[] parts = + text.split("[+,]"); + for (String part : parts) { + part = part.trim(); + if (part.isEmpty()) continue; + + // 跳过经济文本 + if (extractEconomyAmount(part) > 0) + continue; + + int amount = 1; + String itemName = part; + + // 解析 *N 数量 + if (part.contains("*")) { + String[] sp = + part.split("\\*"); + itemName = sp[0].trim(); + try { + amount = Integer.parseInt( + sp[1].trim()); + } catch (Exception e) { + amount = 1; + } + } + + List mats = + matchMaterials(itemName); + for (Material m : mats) { + int give = m.name() + .contains("HELMET") + || m.name() + .contains("CHESTPLATE") + || m.name() + .contains("LEGGINGS") + || m.name() + .contains("BOOTS") + ? 1 : amount; + result.add( + new ItemStack(m, give)); + } + } + return result; + } + + /** + * 中文物品名 → Material 列表 + * 套装返回多个部件 + */ + private List matchMaterials( + String name) { + List list = + new ArrayList<>(); + String n = name.toLowerCase().trim(); + + // 套装 + if (n.contains("皮革套装") + || n.contains("皮套装")) { + list.add(Material.LEATHER_HELMET); + list.add(Material.LEATHER_CHESTPLATE); + list.add(Material.LEATHER_LEGGINGS); + list.add(Material.LEATHER_BOOTS); + return list; + } + if (n.contains("铁套装") + || n.contains("铁甲")) { + list.add(Material.IRON_HELMET); + list.add(Material.IRON_CHESTPLATE); + list.add(Material.IRON_LEGGINGS); + list.add(Material.IRON_BOOTS); + return list; + } + if (n.contains("钻石套装") + || n.contains("钻甲")) { + list.add(Material.DIAMOND_HELMET); + list.add(Material.DIAMOND_CHESTPLATE); + list.add(Material.DIAMOND_LEGGINGS); + list.add(Material.DIAMOND_BOOTS); + return list; + } + // 木制工具套装 + if (n.contains("木制工具") + || n.contains("木工具")) { + list.add(Material.WOODEN_SWORD); + list.add(Material.WOODEN_PICKAXE); + list.add(Material.WOODEN_AXE); + list.add(Material.WOODEN_SHOVEL); + return list; + } + if (n.contains("石制工具") + || n.contains("石工具")) { + list.add(Material.STONE_SWORD); + list.add(Material.STONE_PICKAXE); + list.add(Material.STONE_AXE); + list.add(Material.STONE_SHOVEL); + return list; + } + if (n.contains("铁工具")) { + list.add(Material.IRON_SWORD); + list.add(Material.IRON_PICKAXE); + list.add(Material.IRON_AXE); + list.add(Material.IRON_SHOVEL); + return list; + } + + // 单个物品 + if (n.contains("木剑")) + list.add(Material.WOODEN_SWORD); + else if (n.contains("木镐")) + list.add(Material.WOODEN_PICKAXE); + else if (n.contains("铁剑")) + list.add(Material.IRON_SWORD); + else if (n.contains("铁镐")) + list.add(Material.IRON_PICKAXE); + else if (n.contains("钻石剑")) + list.add(Material.DIAMOND_SWORD); + else if (n.contains("钻石镐")) + list.add(Material.DIAMOND_PICKAXE); + else if (n.contains("钻石")) + list.add(Material.DIAMOND); + else if (n.contains("铁锭")) + list.add(Material.IRON_INGOT); + else if (n.contains("金锭") + || n.contains("金块")) + list.add(Material.GOLD_INGOT); + else if (n.contains("煤炭")) + list.add(Material.COAL); + else if (n.contains("木棍")) + list.add(Material.STICK); + else if (n.contains("弓")) + list.add(Material.BOW); + else if (n.contains("箭")) + list.add(Material.ARROW); + else if (n.contains("床")) + list.add(Material.RED_BED); + else { + // 尝试直接匹配 Material 名 + try { + list.add(Material.valueOf( + n.toUpperCase() + .replaceAll("[^A-Z_]", + ""))); + } catch (Exception e) { + // 未知物品 + } + } + return list; + } + + private QuestFile getOrCreateStage( + TreeMap map, + int key, + String category) { + if (!map.containsKey(key)) { + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + key; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.details = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + } + return map.get(key); + } + + /** + * 单个文件的区块 → 阶段 + * defaultStageNum: 文件无阶段标题时的默认编号 + */ + private void buildSingleFileStages( + List
    sections, + String category, + TreeMap stageMap, + int defaultStageNum) { + Integer curKey = null; + + for (Section sec : sections) { + Integer stageNum = + extractStageNumber(sec.title); + + if (stageNum != null) { + curKey = stageNum; + } else if (curKey == null) { + curKey = defaultStageNum; + } + + QuestFile qf = getOrCreateStage( + stageMap, curKey, category); + + String displayName = + extractDisplayName(sec.title); + + // 设置显示名 + if (!displayName.isEmpty() + && qf.displayName + .startsWith("阶段 ")) { + qf.displayName = displayName; + } + + // 路由 + if (stageNum != null + && sec.lines.isEmpty()) { + if (!displayName.isEmpty()) { + qf.displayName = displayName; + } + } + else if (isRewardTitle(sec.title)) { + // 奖励 → 只进 rewards + qf.rewards.addAll(sec.lines); + } + else if (isDetailsTitle(sec.title)) { + // 详情 → 进 details + conditions + qf.details.addAll(sec.lines); + for (String line : sec.lines) { + if (!qf.conditions + .contains(line)) { + qf.conditions.add(line); + } + } + } + else if (isConditionsTitle( + sec.title)) { + // 条件 → 只进 conditions + qf.conditions.addAll(sec.lines); + } + else { + // 其他 → 条件 + qf.conditions.addAll(sec.lines); + } + } + } + + /** + * "主线任务-斗志昂扬" → "斗志昂扬" + * "支线-钓鱼" → "钓鱼" + * "阶段 1" → "阶段 1" + * "第一阶段" → "第一阶段" + */ + private String extractDisplayName( + String title) { + if (title == null) return ""; + // 按 - 或 — 或 – 分割 + String[] parts = title.split( + "\\s*[-—–]\\s*", 2); + if (parts.length >= 2) { + String right = parts[1].trim(); + if (!right.isEmpty()) { + return right; + } + } + return title; + } + private DatabaseManager getDb() { + return ((Main) plugin).getDb(); + } + + private boolean isDetailsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("任务详情") + || t.contains("详情") + || t.contains("任务说明") + || t.contains("任务目标") + || t.contains("description") + || t.contains("任务要求"); + } + + private boolean isConditionsTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("解锁条件") + || t.contains("条件") + || t.contains("前置条件") + || t.contains("前置要求") + || t.contains("解锁") + || t.contains("condition"); + } + + + private void ensureStage( + TreeMap map, + String category, + QuestFile current, + int counter) { + if (current != null) return; + // 用大数字确保排在有编号阶段后面 + int key = 1000 + counter; + QuestFile qf = new QuestFile(); + qf.displayName = "阶段 " + (counter + 1); + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + map.put(key, qf); + // 更新调用方的引用(通过返回值) + // 这里用 map 操作,current 无法直接更新 + // 所以改用返回值方式 + } + /** + * 自动将条件中的奖励内容移到 rewards + */ + private void separateRewards(QuestFile qf) { + List remaining = + new ArrayList<>(); + for (String line : qf.conditions) { + if (isRewardContent(line)) { + if (!qf.rewards.contains(line)) { + qf.rewards.add(line); + } + } else { + remaining.add(line); + } + } + qf.conditions = remaining; + } + private boolean checkStageDependency( + String player, + String condition) { + Matcher m = Pattern.compile( + "第([一二三四五六七八九十\\d]+)" + + "阶段") + .matcher(condition); + + while (m.find()) { + String numStr = m.group(1); + int stageNum = + parseChineseNumber(numStr); + if (stageNum == 0) { + try { + stageNum = + Integer.parseInt(numStr); + } catch (Exception e) { + return false; + } + } + + boolean stageDone = false; + for (List quests + : questCache.values()) { + for (QuestFile qf : quests) { + Integer qfNum = + extractStageNumber( + qf.displayName); + if (qfNum != null + && qfNum == stageNum) { + if (isStageComplete(player, + qf.conditions)) { + stageDone = true; + } + break; + } + } + if (stageDone) break; + } + + if (!stageDone) return false; + } + return true; + } + + /** + * 判断一行内容是否为奖励 + */ + private boolean isRewardContent( + String line) { + String l = line.toLowerCase(); + // 含货币关键词 + if (l.contains("游戏币") + || l.contains("金币") + || l.contains("积分") + || l.contains("钻石") + || l.contains("发钱") + || l.contains("发放") + || l.contains("经验") + || l.contains("等级") + || l.contains("经济") + || l.contains("余额") + || l.contains("块钱")) + return true; + // 纯数字+单位(如 "500游戏币") + if (l.matches( + ".*\\d+\\s*(游戏币|金币|元|积分|" + + "钻石|块钱|经验|级|经济|余额).*")) + return true; + // $前缀(如 "$500") + if (l.matches(".*\\$\\d+.*")) + return true; + // 范围格式(如 "1000-5000" 或 "1000~5000") + if (l.matches(".*\\d+[-~]\\d+.*") + && !l.contains("阶段") + && !l.contains("stage")) + return true; + return false; + } + + // ========== 标题分类 ========== + + private boolean isStageTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("阶段") + || t.contains("stage") + || t.contains("第一") + || t.contains("第二") + || t.contains("第三") + || t.contains("第四") + || t.contains("第五"); + } + + private boolean isRewardTitle( + String title) { + String t = title.toLowerCase(); + return t.contains("奖励") + || t.contains("奖品") + || t.contains("报酬") + || t.contains("reward") + || t.contains("奖赏") + || t.contains("礼包内容") + || t.contains("礼包") + || t.contains("物品奖励"); + } + + + private QuestFile makeDefaultStage( + String category) { + QuestFile qf = new QuestFile(); + qf.displayName = "第一阶段"; + qf.fileName = category; + qf.conditions = new ArrayList<>(); + qf.rewards = new ArrayList<>(); + return qf; + } + + private static class Section { + final String title; + final List lines; + Section(String title, + List lines) { + this.title = title; + this.lines = lines; + } + } + + // ========== 公开查询 ========== + + public List getQuests( + String category) { + List list = + questCache.get(category); + return list != null ? list + : new ArrayList<>(); + } + + public int getCompletedStageCount( + String player, + List quests) { + int count = 0; + if (quests == null) return 0; + for (QuestFile qf : quests) { + if (isStageComplete(player, + qf.conditions)) { + count++; + } + } + return count; + } + + public QuestFile getCurrentStage( + String player, + List quests) { + if (quests == null) return null; + for (QuestFile qf : quests) { + if (!isStageComplete( + player, qf.conditions)) { + return qf; + } + } + return null; + } + + public int getStageNumber( + List quests, + QuestFile qf) { + if (quests == null || qf == null) + return 0; + return quests.indexOf(qf) + 1; + } + + public int getStageNumber( + QuestFile qf) { + if (qf == null) return 0; + List quests = + getQuests(qf.fileName); + return getStageNumber(quests, qf); + } + + public boolean isStageCompleted( + String player, + QuestFile qf) { + if (qf == null) return false; + return isStageComplete( + player, qf.conditions); + } + + public List getUnmetConditions( + String player, + List conditions) { + List unmet = + new ArrayList<>(); + if (conditions == null) return unmet; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) { + unmet.add( + getProgress( + player, cond)); + } + } + return unmet; + } + public String getProgress( + String player, String rawCond) { + if (player == null || rawCond == null) + return "§7未知"; + String key = stripOptional(rawCond); + + if (key.contains("打卡") + && key.contains("群系")) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return "§7打卡 " + biome + + "群系: §e" + + catCount + "/1"; + } + int total = getCounter( + player, "biome_ALL"); + return "§7群系打卡: §e" + + total + "/1"; + } + + if (key.contains("完成") + && key.contains("阶段")) { + boolean done = checkStageDependency( + player, key); + return done + ? "§a✓ " + key + : "§c✗ " + key; + } + + if ("完成注册登录".equals(key)) { + boolean done = getDb() != null + && getDb().userExists( + player); + return done + ? "§a✓ 完成注册登录" + : "§c✗ 完成注册登录"; + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + boolean done = val != null + && !val.toString().isEmpty(); + return done + ? "§a✓ 绑定Email" + : "§c✗ 绑定Email"; + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + needed + "小时"; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + int cur = getCounter( + player, "CHAT"); + return "§7聊天: §e" + + cur + "/1"; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return "§7方块操作: §e" + + total + "/" + needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return "§7连续登录: §e" + + streak + "/" + needed + "天"; + } + + if (key.startsWith("MINE:")) { + String[] parts = key.split(":"); + if (parts.length >= 3) { + int needed = + parseInt(parts[2]); + int cur = getCounter( + player, + "MINE:" + parts[1]); + return "§7挖 " + parts[1] + + ": §e" + cur + + "/" + needed; + } + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "VILLAGER_TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + + } + + // ===== 击杀泛解析(支持中文列表) ===== +// 匹配 "击杀10只骷髅、僵尸、苦力怕" 等 + if (key.contains("击杀") && key.contains("只")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + + String mobPart = key; + java.util.regex.Matcher killM = + java.util.regex.Pattern + .compile("击杀\\d+只(.+)") + .matcher(key); + if (killM.find()) { + mobPart = killM.group(1).trim(); + } + + String[] mobNames = mobPart + .split("[、,,,]+"); + int totalKills = 0; + StringBuilder names = new StringBuilder(); + for (String name : mobNames) { + String trimmed = name.trim(); + if (trimmed.isEmpty()) continue; + String entityType = + chineseMobToEntity(trimmed); + if (!entityType.isEmpty()) { + totalKills += getCounter( + player, + "KILL:" + entityType); + if (names.length() > 0) + names.append("/"); + names.append(trimmed); + } + } + return "§7击杀" + names + ": §e" + + totalKills + "/" + needed; + } + +// 单一KILL:xxx:N 格式(原有逻辑保留) + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + int needed = parseInt(p[2]); + int cur = getCounter(player, + "KILL:" + p[1]); + return "§7击杀" + p[1] + ": §e" + + cur + "/" + needed; + } + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "KILL_PLAYER"); + return "§7击杀玩家: §e" + + cur + "/" + needed; + } + + if (key.startsWith("CRAFT")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "CRAFT"); + return "§7合成: §e" + + cur + "/" + needed; + } + + if (key.startsWith("FISH")) { + String[] parts = key.split(":"); + int needed = parts.length >= 2 + ? parseInt(parts[1]) : 1; + int cur = getCounter( + player, "FISH"); + return "§7钓鱼: §e" + + cur + "/" + needed; + } + + if (key.startsWith("PLAYTIME")) { + String[] parts = key.split(":"); + long needed = parts.length >= 2 + ? Long.parseLong(parts[1]) + : 0; + long cur = getCounterLong( + player, "PLAYTIME"); + return "§7在线: §e" + + formatMs(cur) + "/" + + formatMs(needed); + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7上报村庄: §e" + + cur + "/" + needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + int cur = getCounter( + player, "REPORT_VILLAGE"); + return "§7探索村庄: §e" + + cur + "/1"; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return "§7近7天登录: §e" + + streak + "/" + + needed + "天"; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7新手任务: §e" + + done + "/" + + quests.size(); + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + int done = 0; + for (QuestFile qf : quests) { + if (isStageComplete( + player, + qf.conditions)) + done++; + } + return "§7主线任务: §e" + + done + "/" + + quests.size(); + } + // ===== 泛解析:击杀类进度 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < mobTypes.size(); i++) { + total += getCounter(player, + "KILL:" + mobTypes.get(i)); + if (i > 0) names.append("/"); + names.append(reverseMobName( + mobTypes.get(i))); + } + return "§7击杀" + names + ": §e" + + total + "/" + needed; + } + } + + // ===== 泛解析:村民交易进度 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "TRADE"); + return "§7村民交易: §e" + + cur + "/" + needed; + } + // ===== 泛解析:采集/挖矿进度 ===== + if (key.contains("采集") + || key.contains("挖")) { + int needed = + parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + StringBuilder names = + new StringBuilder(); + for (int i = 0; + i < matTypes.size(); i++) { + total += getCounter( + player, + "MINE:" + matTypes + .get(i)); + if (i > 0) + names.append("/"); + names.append( + translateMaterialName( + matTypes + .get(i))); + } + return "§7采集" + names + + ": §e" + total + + "/" + needed; + } + } + + // ===== 泛解析:合成进度 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "CRAFT"); + return "§7合成: §e" + cur + "/" + needed; + } + + // ===== 泛解析:钓鱼进度 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, "FISH"); + return "§7钓鱼: §e" + cur + "/" + needed; + } + + return "§7" + key; + } + private String translateMaterialName( + String name) { + switch (name) { + case "COAL_ORE": + return "煤矿石"; + case "DEEPSLATE_COAL_ORE": + return "深层煤矿石"; + case "IRON_ORE": + return "铁矿石"; + case "DEEPSLATE_IRON_ORE": + return "深层铁矿石"; + case "COPPER_ORE": + return "铜矿石"; + case "DEEPSLATE_COPPER_ORE": + return "深层铜矿石"; + case "GOLD_ORE": + return "金矿石"; + case "DEEPSLATE_GOLD_ORE": + return "深层金矿石"; + case "DIAMOND_ORE": + return "钻石矿石"; + case "DEEPSLATE_DIAMOND_ORE": + return "深层钻石矿石"; + case "EMERALD_ORE": + return "绿宝石矿石"; + case "DEEPSLATE_EMERALD_ORE": + return "深层绿宝石矿石"; + case "LAPIS_ORE": + return "青金石矿石"; + case "DEEPSLATE_LAPIS_ORE": + return "深层青金石矿石"; + case "REDSTONE_ORE": + return "红石矿石"; + case "DEEPSLATE_REDSTONE_ORE": + return "深层红石矿石"; + case "NETHER_GOLD_ORE": + return "下界金矿石"; + case "NETHER_QUARTZ_ORE": + return "下界石英矿石"; + case "ANCIENT_DEBRIS": + return "远古残骸"; + case "COAL": + return "煤炭"; + case "RAW_IRON": + return "粗铁"; + case "RAW_GOLD": + return "粗金"; + case "RAW_COPPER": + return "粗铜"; + case "DIAMOND": + return "钻石"; + case "EMERALD": + return "绿宝石"; + case "LAPIS_LAZULI": + return "青金石"; + case "REDSTONE": + return "红石"; + case "QUARTZ": + return "石英"; + default: + return name; + } + } + + /** + * 中文物种名 → Bukkit EntityType 名 + * 支持击杀泛解析的模糊匹配 + */ + private String chineseMobToEntity(String cn) { + String n = cn.toLowerCase().trim(); + + // 直接别名表 + Map mobMap = new HashMap<>(); + mobMap.put("骷髅", "SKELETON"); + mobMap.put("僵尸", "ZOMBIE"); + mobMap.put("苦力怕", "CREEPER"); + mobMap.put("蜘蛛", "SPIDER"); + mobMap.put("洞穴蜘蛛", "CAVE_SPIDER"); + mobMap.put("小白", "SKELETON"); + mobMap.put("末影人", "ENDERMAN"); + mobMap.put("enderman", "ENDERMAN"); + mobMap.put("猪灵", "PIGLIN"); + mobMap.put("猪灵蛮兵", "PIGLIN_BRUTE"); + mobMap.put("恶魂", "GHAST"); + mobMap.put("凋灵骷髅", "WITHER_SKELETON"); + mobMap.put("烈焰人", "BLAZE"); + mobMap.put("史莱姆", "SLIME"); + mobMap.put("岩浆怪", "MAGMA_CUBE"); + mobMap.put("女巫", "WITCH"); + mobMap.put("蠹虫", "SILVERFISH"); + mobMap.put("潜影贝", "SHULKER"); + mobMap.put("守卫者", "GUARDIAN"); + mobMap.put("远古守卫者", "ELDER_GUARDIAN"); + mobMap.put("溺尸", "DROWNED"); + mobMap.put("僵尸村民", "ZOMBIE_VILLAGER"); + mobMap.put("尸壳", "HUSK"); + mobMap.put("流浪者", "STRAY"); + mobMap.put("幻翼", "PHANTOM"); + mobMap.put("掠夺者", "PILLAGER"); + mobMap.put("卫道士", "VINDICATOR"); + mobMap.put("唤魔者", "EVOKER"); + mobMap.put("恼鬼", "VEX"); + mobMap.put("劫掠兽", "RAVAGER"); + mobMap.put("蜘蛛骑士", "SPIDER_JOCKEY"); + mobMap.put("凋灵", "WITHER"); + mobMap.put("末影龙", "ENDER_DRAGON"); + + // 精确匹配 + if (mobMap.containsKey(n)) { + return mobMap.get(n); + } + + // 模糊匹配(包含关系) + for (Map.Entry en + : mobMap.entrySet()) { + if (n.contains(en.getKey()) + || en.getKey().contains(n)) { + return en.getValue(); + } + } + + // 尝试直接用英文名 + try { + return org.bukkit.entity.EntityType + .valueOf(n.toUpperCase() + .replace(" ", "_")) + .name(); + } catch (Exception e) { + return ""; + } + } + + public List checkInBiome( + String player, + String rawBiomeKey, + String worldName, + double x, double y, double z) { + List matched = + new ArrayList<>(); + + // 32格去重 + String posKey = + player + "_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + String lastPos = + lastCheckInPos.get(posKey); + if (curPos.equals(lastPos)) { + plugin.getLogger().info( + "[QuestTracker] 打卡忽略: " + + player + + " 32格内重复"); + return matched; + } + lastCheckInPos.put(posKey, curPos); + + String category = + mapBiomeCategory( + rawBiomeKey, worldName); + // 结构检测(覆盖纯生物群系分类) + // 地下检测:Y低于表面则归类为地下 +// 覆盖废弃矿井/试炼密室/要塞/远古城市等 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + + // ===== 结构检测(覆盖纯生物群系分类)===== + // 使用 PaperMC API 检测玩家所在位置的结构 + String structureCat = + detectStructureAt( + worldName, x, y, z); + if (structureCat != null + && !structureCat.isEmpty()) { + // 结构检测成功,使用结构分类 + // 但保留"地下"分类(如果Y低于表面) + if (!"地下".equals(category)) { + category = structureCat; + } + plugin.getLogger().info( + "[QuestTracker] 结构覆盖: " + + category + + " (结构: " + + structureCat + ")"); + } + + + // 记录总打卡 + int allCur = getCounter( + player, "biome_ALL"); + setCounter(player, "biome_ALL", + allCur + 1); + + // 记录分类打卡 + if (!category.isEmpty() + && !"主世界" + .equals(category)) { + String catKey = + "biomecat_" + category; + int catCur = getCounter( + player, catKey); + setCounter(player, catKey, + catCur + 1); + } + + // 记录具体群系 + String bioKey = + "biome_" + rawBiomeKey; + int bioCur = getCounter( + player, bioKey); + setCounter(player, bioKey, + bioCur + 1); + + // 检查所有任务条件是否匹配 + List categories = + Arrays.asList( + "新人任务", + "主线任务", + "支线任务"); + + for (String cat : categories) { + List quests = + getQuests(cat); + for (QuestFile qf : quests) { + if (isStageCompleted( + player, qf)) + continue; + for (String cond + : qf.conditions) { + if (isOptional(cond)) + continue; + String condKey = + stripOptional(cond); + if (!condKey + .contains("打卡")) + continue; + // 支持群系和结构两种打卡条件 + boolean isBiomeCond = + condKey.contains("群系") + || condKey + .contains("风景点"); + boolean isStructureCond = + condKey.contains("结构") + || condKey + .contains("村庄") + || condKey + .contains("神殿") + || condKey + .contains("神庙") + || condKey + .contains("小屋") + || condKey + .contains("雪屋") + || condKey + .contains("府邸") + || condKey + .contains("前哨站") + || condKey + .contains("矿井") + || condKey + .contains("要塞") + || condKey + .contains("城市") + || condKey + .contains("废墟") + || condKey + .contains("密室") + || condKey + .contains("沉船") + || condKey + .contains("宝箱") + || condKey + .contains("传送门") + || condKey + .contains("化石") + || condKey + .contains("堡垒"); + if (!isBiomeCond + && !isStructureCond) + continue; + String target = + extractBiome(condKey); + boolean hit = false; + if ("ALL".equals(target) + || "风景点" + .equals(target)) { + hit = true; + } else if (!target.isEmpty() + && target.equals( + category)) { + hit = true; + } + if (hit && !matched + .contains(cond)) { + matched.add(cond); + } + } + } + } + + return matched; + } + + /* + * 打卡入口:报告坐标 → 32格去重 → 匹配条件 → 记录 + */ + public String checkInAtPosition( + String player, String worldName, + double x, double y, double z) { + + List history = + loadCheckInPositions(player); + for (double[] pos : history) { + double dist = Math.sqrt( + Math.pow(x - pos[0], 2) + + Math.pow(y - pos[1], 2) + + Math.pow(z - pos[2], 2)); + if (dist <= 32) { + return "§c距离上次打卡点太近(" + + String.format( + "%.0f", dist) + + "格)"; + } + } + + // ===== 获取群系和分类 ===== + String biome = "minecraft:plains"; + String category = "平原"; + String structureCat = null; + try { + org.bukkit.World w = + Bukkit.getWorld(worldName); + if (w != null) { + org.bukkit.Location loc = + new org.bukkit.Location( + w, + (int) Math.floor(x), + (int) Math.floor(y), + (int) Math.floor(z)); + biome = loc.getBlock() + .getBiome().getKey() + .toString(); + category = mapBiomeCategory( + biome, worldName); + + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + + // ===== 结构检测(覆盖纯生物群系分类)===== + structureCat = + detectStructureAt( + worldName, x, y, z); + if (structureCat != null + && !structureCat.isEmpty() + && !"地下".equals(category)) { + category = structureCat; + } + + if ("下界".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.NETHER) { + category = "下界"; + } else if ("末地".equals(category) + || w.getEnvironment() + == org.bukkit.World + .Environment.THE_END) { + category = "末地"; + } + } + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 群系读取失败: " + + ex.getMessage()); + } + + plugin.getLogger().info( + "[QuestTracker] 打卡: " + + player + + " world=" + worldName + + " biome=" + biome + + " cat=" + category + + " pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + + saveCheckInPosition( + player, x, y, z); + recordCheckIn( + player, biome, category, + worldName, x, y, z); + List matched = + checkInBiome( + player, biome, + worldName, x, y, z); + + StringBuilder sb = new StringBuilder(); + sb.append("§a打卡成功!群系: §e") + .append(category); + if (structureCat != null && !structureCat.isEmpty()) { + sb.append("\n§a结构: §e").append(structureCat); + } + if (!matched.isEmpty()) { + sb.append("\n§a完成任务条件:"); + for (String m : matched) { + String qn = + findQuestName(m); + sb.append("\n§7• "); + if (qn != null) { + sb.append("§e[") + .append(qn) + .append("§7] §f"); + } + sb.append(m); + } + } + return sb.toString(); + } + + + /** + * 根据条件文本查找所属任务名 + */ + private String findQuestName( + String condition) { + for (String cat : Arrays.asList( + "新人任务", + "主线任务", + "支线任务")) { + for (QuestFile qf : + getQuests(cat)) { + if (qf.conditions.contains( + condition)) { + return qf.displayName; + } + } + } + return null; + } + + + /** + * 加载玩家所有历史打卡坐标 + * 格式: x1,y1,z1;x2,y2,z2;... + */ + private List loadCheckInPositions( + String player) { + List list = new ArrayList<>(); + Object val = storage.getCondition( + player, "checkin_all_pos"); + if (val == null) return list; + String data = val.toString(); + if (data.isEmpty()) return list; + String[] records = data.split(";"); + for (String r : records) { + String[] parts = r.split(","); + if (parts.length >= 3) { + try { + list.add(new double[]{ + Double.parseDouble(parts[0]), + Double.parseDouble(parts[1]), + Double.parseDouble(parts[2])}); + } catch (NumberFormatException e) { + // 跳过损坏数据 + } + } + } + return list; + } + + /** + * 保存打卡坐标(追加到已有记录) + */ + private void saveCheckInPosition( + String player, double x, + double y, double z) { + List list = + loadCheckInPositions(player); + list.add(new double[]{x, y, z}); + StringBuilder sb = new StringBuilder(); + for (double[] pos : list) { + if (sb.length() > 0) sb.append(";"); + sb.append(pos[0]).append(",") + .append(pos[1]).append(",") + .append(pos[2]); + } + storage.setConditionRaw( + player, + "checkin_all_pos", + sb.toString()); + } + // ========== 中文生物名 → EntityType 映射 ========== + private static final Map + MOB_NAME_MAP = new HashMap<>(); + static { + MOB_NAME_MAP.put("洞穴蜘蛛", "CAVE_SPIDER"); + MOB_NAME_MAP.put("骷髅", "SKELETON"); + MOB_NAME_MAP.put("僵尸", "ZOMBIE"); + MOB_NAME_MAP.put("苦力怕", "CREEPER"); + MOB_NAME_MAP.put("蜘蛛", "SPIDER"); + MOB_NAME_MAP.put("末影人", "ENDERMAN"); + MOB_NAME_MAP.put("女巫", "WITCH"); + MOB_NAME_MAP.put("烈焰人", "BLAZE"); + MOB_NAME_MAP.put("僵尸猪灵", + "ZOMBIFIED_PIGLIN"); + MOB_NAME_MAP.put("猪灵", "PIGLIN"); + MOB_NAME_MAP.put("恶魂", "GHAST"); + MOB_NAME_MAP.put("凋灵骷髅", + "WITHER_SKELETON"); + MOB_NAME_MAP.put("史莱姆", "SLIME"); + MOB_NAME_MAP.put("岩浆怪", "MAGMA_CUBE"); + MOB_NAME_MAP.put("溺尸", "DROWNED"); + MOB_NAME_MAP.put("尸壳", "HUSK"); + MOB_NAME_MAP.put("流髑", "STRAY"); + MOB_NAME_MAP.put("幻翼", "PHANTOM"); + MOB_NAME_MAP.put("守卫者", "GUARDIAN"); + MOB_NAME_MAP.put("远古守卫者", + "ELDER_GUARDIAN"); + MOB_NAME_MAP.put("掠夺者", "PILLAGER"); + MOB_NAME_MAP.put("卫道士", "VINDICATOR"); + MOB_NAME_MAP.put("唤魔者", "EVOKER"); + MOB_NAME_MAP.put("恼鬼", "VEX"); + MOB_NAME_MAP.put("劫掠兽", "RAVAGER"); + MOB_NAME_MAP.put("潜影贝", "SHULKER"); + MOB_NAME_MAP.put("末影螨", "ENDERMITE"); + MOB_NAME_MAP.put("凋灵", "WITHER"); + MOB_NAME_MAP.put("末影龙", "ENDER_DRAGON"); + MOB_NAME_MAP.put("监守者", "WARDEN"); + MOB_NAME_MAP.put("嗅探兽", "SNIFFER"); + MOB_NAME_MAP.put("僵尸村民", + "ZOMBIE_VILLAGER"); + } + + /** + * 从条件文本中提取所有匹配的生物类型 + * 按名称长度降序匹配,避免短名误匹配 + */ + private List extractMobTypes( + String text) { + List types = new ArrayList<>(); + List> sorted = + new ArrayList<>( + MOB_NAME_MAP.entrySet()); + sorted.sort((a, b) -> + b.getKey().length() + - a.getKey().length()); + String remaining = text; + for (Map.Entry entry + : sorted) { + if (remaining.contains( + entry.getKey())) { + if (!types.contains( + entry.getValue())) { + types.add(entry.getValue()); + } + remaining = remaining.replace( + entry.getKey(), ""); + } + } + return types; + } + + /** + * EntityType → 中文名 + */ + private String reverseMobName( + String entityType) { + for (Map.Entry entry + : MOB_NAME_MAP.entrySet()) { + if (entry.getValue() + .equals(entityType)) { + return entry.getKey(); + } + } + return entityType; + } + + /** + * 从条件文本中提取矿物名 + * "采集任意矿物(煤炭除外)1组" + * → [COAL_ORE, IRON_ORE, GOLD_ORE, ...] + */ + private List extractMaterialTypes( + String text) { + List types = new ArrayList<>(); + // 排除列表 + List excludes = + new ArrayList<>(); + if (text.contains("煤炭除外")) { + excludes.add("COAL"); + excludes.add("COAL_ORE"); + excludes.add("DEEPSLATE_COAL_ORE"); + } + // 通配符:"矿物" "矿石" + if (text.contains("矿物") + || text.contains("矿石")) { + String[] ores = { + "COAL_ORE", + "DEEPSLATE_COAL_ORE", + "IRON_ORE", + "DEEPSLATE_IRON_ORE", + "COPPER_ORE", + "DEEPSLATE_COPPER_ORE", + "GOLD_ORE", + "DEEPSLATE_GOLD_ORE", + "DIAMOND_ORE", + "DEEPSLATE_DIAMOND_ORE", + "EMERALD_ORE", + "DEEPSLATE_EMERALD_ORE", + "LAPIS_ORE", + "DEEPSLATE_LAPIS_ORE", + "REDSTONE_ORE", + "DEEPSLATE_REDSTONE_ORE", + "NETHER_GOLD_ORE", + "NETHER_QUARTZ_ORE", + "ANCIENT_DEBRIS" + }; + for (String ore : ores) { + if (!excludes.contains(ore)) { + types.add(ore); + } + } + return types; + } + // 单个矿物名 + if (text.contains("煤炭")) + types.add("COAL"); + if (text.contains("铁矿") + || text.contains("铁锭")) + types.add("RAW_IRON"); + if (text.contains("金矿") + || text.contains("金锭")) + types.add("RAW_GOLD"); + if (text.contains("钻石")) + types.add("DIAMOND"); + if (text.contains("绿宝石")) + types.add("EMERALD"); + if (text.contains("青金石")) + types.add("LAPIS_LAZULI"); + if (text.contains("红石")) + types.add("REDSTONE"); + if (text.contains("铜")) + types.add("RAW_COPPER"); + if (text.contains("下界石英") + || text.contains("石英")) + types.add("QUARTZ"); + return types; + } + + + /** + * 从条件文本中提取目标群系分类 + * "打卡上报地狱任意群系" → "下界" + * "打卡任意海洋群系" → "海洋" + * "打卡任意群系" → "ALL" + * "打卡任意风景点" → "" + * "打卡村庄" → "村庄" + * "打卡沙漠神殿" → "沙漠" + */ + private String extractBiome(String text) { + if (text == null) return ""; + + // 优先匹配结构名称(更具体) + String structureResult = + mapStructureCategory(text); + if (!structureResult.isEmpty()) { + return structureResult; + } + + // 逐个匹配分类关键词 + for (Map.Entry en + : CATEGORY_KEYWORDS.entrySet()) { + if (text.contains(en.getKey())) { + return en.getValue(); + } + } + if (text.contains("风景点") + || text.contains("风景")) { + return "ALL"; + } + + // 有"群系"但无具体分类 → ALL + if (text.contains("群系")) { + return "ALL"; + } + return ""; + } + + + // ========== 标记条件 ========== + + public void markConditionComplete( + String player, + String conditionName) { + if (player == null + || conditionName == null) + return; + storage.setCondition( + player, conditionName); + } + + // ========== 事件回调 ========== + + public void onBlockMined( + String player, String material) { + String key = "MINE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + // 总计数 + setCounter(player, "MINE:ALL", + getCounter(player, "MINE:ALL") + + 1); + } + public boolean onReportVillage( + String player, + String worldName, + double x, double y, double z) { + String dbKey = "village_lastPos"; + String curPos = worldName + + "|" + String.format( + "%.0f", x / 32) + + "|" + String.format( + "%.0f", y / 32) + + "|" + String.format( + "%.0f", z / 32); + Object lastObj = + storage.getCondition( + player, dbKey); + String lastPos = lastObj != null + ? lastObj.toString() : ""; + if (curPos.equals(lastPos)) { + return false; + } + storage.setConditionRaw( + player, dbKey, curPos); + + String key = "REPORT_VILLAGE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + return true; + } + + public void onMobKill( + String player, String mobType) { + String key = "KILL:" + mobType; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayerKill( + String player) { + String key = "KILL_PLAYER"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onItemCrafted( + String player) { + String key = "CRAFT"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onFishCaught( + String player) { + String key = "FISH"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + public void onPlayTime( + String player, long ms) { + String key = "PLAYTIME"; + long cur = getCounterLong( + player, key); + setCounter(player, key, cur + ms); + } + + // ========== 内部:条件判定 ========== + + private boolean isStageComplete( + String player, + List conditions) { + if (conditions == null + || conditions.isEmpty()) + return true; + for (String cond : conditions) { + if (isOptional(cond)) continue; + if (!isConditionMet( + player, cond)) + return false; + } + return true; + } + + public boolean isConditionMet( + String player, + String rawCond) { + if (player == null + || rawCond == null) + return false; + + String key = stripOptional(rawCond); + + // 无条件类文本直接通过 + if (key.equals("无前级条件") + || key.equals("无条件") + || key.equals("无") + || key.equals("none") + || key.equals("无需条件") + || key.equals("无解锁条件")) { + return true; + } + + if (key.contains("打卡") + && (key.contains("群系") + || key.contains("风景点") + || key.contains("结构") + || key.contains("村庄") + || key.contains("神殿") + || key.contains("神庙") + || key.contains("小屋") + || key.contains("雪屋") + || key.contains("府邸") + || key.contains("前哨站") + || key.contains("矿井") + || key.contains("要塞") + || key.contains("城市") + || key.contains("废墟") + || key.contains("密室") + || key.contains("废墟") + || key.contains("沉船") + || key.contains("宝箱") + || key.contains("传送门") + || key.contains("化石") + || key.contains("堡垒"))) { + String biome = + extractBiome(key); + if ("ALL".equals(biome)) { + int total = getCounter( + player, "biome_ALL"); + return total >= 1; + } + if (!biome.isEmpty()) { + int catCount = getCounter( + player, + "biomecat_" + biome); + return catCount >= 1; + } + return getCounter( + player, "biome_ALL") >= 1; + } + + if (key.contains("完成") + && key.contains("阶段")) { + return checkStageDependency( + player, key); + } + + if ("完成注册登录".equals(key)) { + return getDb() != null + && getDb().userExists( + player); + } + + if (key.contains("绑定Email") + || key.contains("绑定邮箱")) { + Object val = + getDb().getField( + player, "email"); + return val != null + && !val.toString().isEmpty(); + } + + if (key.contains("在线") + && key.contains("小时")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + long cur = getCounterLong( + player, "PLAYTIME"); + return cur >= needed * 3600000L; + } + + if (key.contains("打招呼") + || key.contains("打个招呼") + || key.contains("发消息") + || key.contains("聊天")) { + return getCounter( + player, "CHAT") >= 1; + } + + if ((key.contains("破坏") + || key.contains("放置")) + && key.contains("方块")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int total = getCounter( + player, "MINE:ALL") + + getCounter( + player, "PLACE:ALL"); + return total >= needed; + } + + if (key.contains("连续") + && key.contains("登录")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + Object val = + getDb().getField( + player, + "checkin_streak"); + int streak = val instanceof Number + ? ((Number) val).intValue() + : 0; + return streak >= needed; + } + + if (key.startsWith("MINE:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "MINE:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.startsWith("KILL:")) { + String[] p = key.split(":"); + if (p.length >= 3) { + return getCounter(player, + "KILL:" + p[1]) + >= parseInt(p[2]); + } + } + + if (key.contains("村民") + && key.contains("交易")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "VILLAGER_TRADE") + >= needed; + } + + + if (key.startsWith("KILL_PLAYER")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter(player, + "KILL_PLAYER") >= n; + } + + if (key.startsWith("CRAFT")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "CRAFT") >= n; + } + + if (key.startsWith("FISH")) { + String[] p = key.split(":"); + int n = p.length >= 2 + ? parseInt(p[1]) : 1; + return getCounter( + player, "FISH") >= n; + } + + if (key.startsWith("PLAYTIME")) { + String[] p = key.split(":"); + long n = p.length >= 2 + ? Long.parseLong(p[1]) : 0; + return getCounterLong(player, + "PLAYTIME") >= n; + } + + if (key.contains("上报") + && key.contains("村庄")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter( + player, "REPORT_VILLAGE") + >= needed; + } + + if (key.contains("探索") + && key.contains("村庄")) { + return getCounter( + player, "REPORT_VILLAGE") + >= 1; + } + + if (key.contains("7天") + || key.contains("七天")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int streak = getIntField( + player, + "checkin_streak"); + if (streak == 0) { + streak = getIntField( + player, "streak"); + } + if (streak == 0) { + streak = getIntField( + player, + "total_checkin_days"); + } + return streak >= needed; + } + + if (key.contains("新手任务") + && key.contains("全部")) { + List quests = + getQuests("新人任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + + if (key.contains("主线任务") + && key.contains("全部")) { + List quests = + getQuests("主线任务"); + for (QuestFile qf : quests) { + if (!isStageComplete( + player, + qf.conditions)) + return false; + } + return true; + } + // ===== 泛解析:击杀类中文条件 ===== + if (key.contains("击杀") || key.contains("猎杀") + || key.contains("消灭")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List mobTypes = extractMobTypes(key); + if (!mobTypes.isEmpty()) { + int total = 0; + for (String mobType : mobTypes) { + total += getCounter(player, + "KILL:" + mobType); + } + return total >= needed; + } + } + + // ===== 泛解析:村民交易条件 ===== + if (key.contains("交易") + && (key.contains("村民") + || key.contains("与村民"))) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "TRADE") + >= needed; + } + + // ===== 泛解析:采集/挖矿类条件 ===== + if (key.contains("采集") || key.contains("挖")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + List matTypes = + extractMaterialTypes(key); + if (!matTypes.isEmpty()) { + int total = 0; + for (String mat : matTypes) { + total += getCounter(player, + "MINE:" + mat); + } + return total >= needed; + } + } + + // ===== 泛解析:合成类条件 ===== + if (key.contains("合成") || key.contains("制作")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "CRAFT") + >= needed; + } + + // ===== 泛解析:钓鱼类条件 ===== + if (key.contains("钓鱼") || key.contains("钓")) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + return getCounter(player, "FISH") + >= needed; + } + + return false; + } + + public void debugLogin(String player) { + try { + var user = getDb().getUser(player); + if (user != null) { + plugin.getLogger().info( + "[QuestTracker] " + + "=== " + player + + " 数据库全部字段 ==="); + for (var entry + : user.entrySet()) { + plugin.getLogger().info( + "[QuestTracker] " + + entry.getKey() + + " = " + + entry.getValue() + + " (" + + (entry.getValue() + != null + ? entry.getValue() + .getClass() + .getSimpleName() + : "null") + + ")"); + } + } else { + plugin.getLogger().info( + "[QuestTracker] " + + "getUser返回null: " + + player); + } + } catch (Exception e) { + plugin.getLogger().info( + "[QuestTracker] " + + "debug异常: " + + e.getMessage()); + } + } + + // ========== 内部:计数器 ========== + + private int getCounter( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + return 0; + } + private int getIntField( + String player, String field) { + try { + Object val = getDb().getField( + player, field); + if (val instanceof Number) { + return ((Number) val).intValue(); + } + if (val != null) { + return Integer.parseInt( + val.toString().trim()); + } + } catch (Exception e) { + // 忽略 + } + return 0; + } + + public void onVillagerTrade(String player) { + String key = "VILLAGER_TRADE"; + int cur = getCounter(player, key); + setCounter(player, key, cur + 1); + } + + private long getCounterLong( + String player, String key) { + Object val = storage.getCondition( + player, "cnt_" + key); + if (val instanceof Number) { + return ((Number) val).longValue(); + } + return 0; + } + + private void setCounter( + String player, String key, + int value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + private void setCounter( + String player, String key, + long value) { + storage.setConditionRaw( + player, "cnt_" + key, + String.valueOf(value)); + } + + // ========== 工具 ========== + + private boolean toBool(Object val) { + if (val == null) return false; + if (val instanceof Boolean) + return (Boolean) val; + if (val instanceof Number) + return ((Number) val) + .intValue() != 0; + String s = String.valueOf(val); + return "1".equals(s) + || "true" + .equalsIgnoreCase(s); + } + + public boolean isOptional(String cond) { + if (cond == null) return false; + String c = cond; + return c.contains("(进阶)") + || c.contains("(可选)") + || c.contains(",可选)") + || c.contains(",可选)") + || c.contains("【可选】") + || c.contains("【进阶】"); + } + + + private String stripOptional(String cond) { + if (cond == null) return ""; + return cond.replace("(进阶)", "") + .replace("(可选)", "") + .replace(",可选)", "") + .replace(",可选)", "") + .replace("【可选】", "") + .replace("【进阶】", "") + .trim(); + } + + private int parseInt(String s) { + + try { + return Integer.parseInt(s.trim()); + } catch (Exception e) { + return 0; + } + } + private Object getDbField( + String player, String field) { + Main main = (Main) plugin; + return main.getDb().getField( + player, field); + } + + /** + * 从文本中提取第一个数字 + * "在线时间≥1小时" → 1 + * "破坏/放置方块100个" → 100 + */ + private int parseNumberFromText( + String text) { + java.util.regex.Matcher m = + java.util.regex.Pattern + .compile("(\\d+)") + .matcher(text); + if (m.find()) { + try { + return Integer.parseInt( + m.group(1)); + } catch (Exception e) { + return 0; + } + } + return 0; + } + public void onBlockPlaced( + String player, String material) { + String key = "PLACE:" + material; + setCounter(player, key, + getCounter(player, key) + 1); + setCounter(player, "PLACE:ALL", + getCounter(player, "PLACE:ALL") + + 1); + } + + public void onPlayerChat( + String player) { + String key = "CHAT"; + setCounter(player, key, + getCounter(player, key) + 1); + } + + private String formatMs(long ms) { + long sec = ms / 1000; + long min = sec / 60; + long hr = min / 60; + if (hr > 0) + return hr + "小时" + + (min % 60) + "分"; + if (min > 0) + return min + "分" + + (sec % 60) + "秒"; + return sec + "秒"; + } + + // ========== 数据结构 ========== + + public static class QuestFile { + public String displayName = ""; + public String fileName = ""; + public List details = + new ArrayList<>(); + public List rewards = + new ArrayList<>(); + public List conditions = + new ArrayList<>(); + + public boolean hasConditions() { + return conditions != null + && !conditions.isEmpty(); + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioDownloadListener.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioDownloadListener.java new file mode 100644 index 0000000..d3e744f --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioDownloadListener.java @@ -0,0 +1,144 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player + .PlayerResourcePackStatusEvent; + +import java.util.UUID; +import java.util.concurrent + .ConcurrentHashMap; + +public class RadioDownloadListener + implements Listener { + + private final Main plugin; + private final java.util.logging.Logger log; + + private final ConcurrentHashMap + failCount = new ConcurrentHashMap<>(); + private final ConcurrentHashMap + blocked = new ConcurrentHashMap<>(); + + private static final int MAX_FAILED = 3; + + public RadioDownloadListener(Main plugin) { + this.plugin = plugin; + this.log = plugin.getLogger(); + } + + public void onPlayerQuit(UUID uuid) { + failCount.remove(uuid); + blocked.remove(uuid); + } + + @EventHandler + public void onStatus( + PlayerResourcePackStatusEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + String status = + e.getStatus().name(); + + log.info("[Radio] === 资源包状态 ==="); + log.info("[Radio] 玩家: " + + p.getName()); + log.info("[Radio] UUID: " + uuid); + log.info("[Radio] 状态: " + + status); + log.info("[Radio] 原始状态枚举: " + + e.getStatus().getClass() + .getSimpleName() + + "." + + e.getStatus().name()); + + if (blocked.containsKey(uuid)) { + log.info("[Radio] 已熔断,跳过: " + + p.getName()); + return; + } + + switch (status) { + case "SUCCESS": + log.info("[Radio] " + + p.getName() + + " ✓ 资源包加载成功"); + failCount.remove(uuid); + blocked.remove(uuid); + break; + + case "ACCEPTED": + log.info("[Radio] " + + p.getName() + + " 客户端已接受," + + "等待下载..."); + break; + + case "DECLINED": + log.warning("[Radio] " + + p.getName() + + " ✗ 拒绝了资源包"); + log.warning("[Radio] " + + "可能原因: " + + "1.客户端设置禁用资源包" + + " 2.已缓存拒绝记录" + + " 3.SSL证书不匹配"); + handleFailure(p, uuid); + break; + + case "FAILED_DOWNLOAD": + log.warning("[Radio] " + + p.getName() + + " ✗ 下载失败"); + log.warning("[Radio] " + + "可能原因: " + + "1.URL无法访问" + + " 2.SSL证书错误" + + " 3.网络超时" + + " 4.文件不存在(404)"); + handleFailure(p, uuid); + break; + + default: + log.info("[Radio] " + + p.getName() + + " 未知状态: " + + status); + break; + } + } + + private void handleFailure(Player p, UUID uuid) { + int failNum = failCount.getOrDefault(uuid, 0) + 1; + failCount.put(uuid, failNum); + + log.warning("[Radio] " + p.getName() + + " 失败次数: " + failNum + "/" + MAX_FAILED); + + if (failNum >= MAX_FAILED) { + blocked.put(uuid, true); + log.severe("[Radio] " + p.getName() + + " 已达最大失败次数,停止重试," + + "玩家继续留在服务器"); + // 不踢出,只记录熔断,玩家正常游玩 + p.sendMessage("§e§l[Radio] 资源包加载失败" + + ",已取消下载资源包,部分画面音频可能出现问题" + + "\n§d§l若想继续下载,请退出重进。若持续失败,请上报管理员"); + + return; + } +// 打了包没测试 + p.sendMessage("§e§l[Radio] 下载失败," + + "重试... (" + failNum + "/" + MAX_FAILED + ")"); + + // 5秒后重试 + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline() && plugin.radio != null) { + plugin.radio.sendResourcePack(p); + } + }, 100L); + } +} \ No newline at end of file diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioManager.java new file mode 100644 index 0000000..6fe5eda --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RadioManager.java @@ -0,0 +1,1040 @@ +package Sdf1_login; + +import org.bukkit.*; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; + +import java.io.*; +import java.nio.file.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.zip.*; +import java.util.Collections; +import org.bukkit.NamespacedKey; +import java.util.Set; + + +public class RadioManager { + + private final Main plugin; + private File radioDir; + private File oggDir; + private File packDir; + private BukkitTask mainRadioTask; + private final Map loginPlayers = + new ConcurrentHashMap<>(); + private String externalUrl = ""; + private int httpPort = 443; + private final Set pendingPack = + Collections.newSetFromMap( + new ConcurrentHashMap<>()); + private String lastPlayedSound = ""; + + private int radioIndex = 0; + + // ========== 登录阶段推送(管理员手动触发) ========== + public void startLoginRadio(Player p) { + loginPlayers.put(p.getName(), true); + } + + public void stopLoginRadio(Player p) { + loginPlayers.remove(p.getName()); + } + + public void stopAllLoginRadio() { + loginPlayers.clear(); + } + + private final List mainFiles = new ArrayList<>(); + private final List loginFiles = new ArrayList<>(); + + public RadioManager(Main plugin) { + this.plugin = plugin; + this.radioDir = new File( + plugin.getDataFolder(), "radio"); + this.oggDir = new File( + plugin.getDataFolder(), "radio/ogg"); + this.packDir = new File( + plugin.getDataFolder(), "radiopack"); + radioDir.mkdirs(); + oggDir.mkdirs(); + packDir.mkdirs(); + loadRadioConfig(); + } + // ========== 读取 radio_config.txt ========== + private void loadRadioConfig() { + File cfg = new File( + plugin.getDataFolder(), + "radio_config.txt"); + + // 不存在则创建默认配置 + if (!cfg.exists()) { + createRadioConfig(cfg); + plugin.getLogger().info( + "[Radio] 已生成 radio_config.txt"); + } + + // 读取 + try (BufferedReader r = new BufferedReader( + new InputStreamReader( + new FileInputStream(cfg), + java.nio.charset + .StandardCharsets + .UTF_8))) { + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("#") + || line.trim().isEmpty()) + continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String key = line.substring(0, eq) + .trim(); + String val = line.substring(eq + 1) + .trim(); + if (key.equals("resource-pack-url")) { + externalUrl = val; + } else if (key.equals("http-port")) { + try { + httpPort = Integer.parseInt( + val); + } catch (NumberFormatException ignored) { + } + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 读取radio_config.txt失败: " + + e.getMessage()); + } + + // 443是HTTPS端口,内嵌HTTP不能用 + if (httpPort == 443) httpPort = 8080; + + plugin.getLogger().info( + "[Radio] 配置: externalUrl=[" + + externalUrl + "] httpPort=" + + httpPort); + } + + private void createRadioConfig(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + java.nio.charset + .StandardCharsets + .UTF_8))) { + pw.println("# ============ Sdf1 Radio Config ============"); + pw.println("#"); + pw.println("# resource-pack-url"); + pw.println("# 填外部地址(HTTP或HTTPS均可)"); + pw.println("# 例: https://example.com/radiopack.zip"); + pw.println("# 留空 = 使用内置HTTP服务"); + pw.println("#"); + pw.println("# http-port"); + pw.println("# 内置HTTP端口(仅resource-pack-url为空时生效)"); + pw.println("#"); + pw.println("# ============ Settings ==============="); + pw.println(); + pw.println("resource-pack-url="); + pw.println("http-port=8080"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 创建radio_config.txt失败: " + + e.getMessage()); + } + } + + // ========== 初始化 ========== + public void init() { + loadRadioConfig(); + convertNonOgg(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); // 启动HTTP + plugin.getLogger().info( + "[Radio] 初始化完成: 全服=" + + mainFiles.size() + + " 登录=" + loginFiles.size()); + } + + + // ========== 转换非ogg文件 ========== + private void convertNonOgg() { + convertDir(radioDir); + convertDir(new File(radioDir, "login")); + convertDir(new File(radioDir, "reg")); + } + + private void convertDir(File src) { + if (!src.exists() || !src.isDirectory()) return; + File[] files = src.listFiles((d, n) -> { + String l = n.toLowerCase(); + return l.endsWith(".mp3") + || l.endsWith(".wav") + || l.endsWith(".flac"); + }); + if (files == null) return; + + for (File f : files) { + String oggName = f.getName() + .replaceAll("\\.(mp3|wav|flac)$", + ".ogg"); + File oggFile = new File(src, oggName); + if (oggFile.exists() + && oggFile.lastModified() + >= f.lastModified()) { + continue; + } + // 调用ffmpeg转换 + if (convertWithFfmpeg(f, oggFile)) { + plugin.getLogger().info( + "[Radio] 转换成功: " + + f.getName()); + } else { + plugin.getLogger().warning( + "[Radio] 转换失败: " + + f.getName() + + " (需要安装ffmpeg)"); + } + } + } + + private boolean convertWithFfmpeg( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-c:a", "libvorbis", + "-q:a", "5", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean finished = + p.waitFor(30, TimeUnit.SECONDS); + if (!finished) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + return false; + } + } + + // ========== 加载ogg文件列表 ========== + private void loadAllOggs() { + mainFiles.clear(); + loginFiles.clear(); + loadOggsFlat(radioDir, mainFiles); + loadOggsFlat( + new File(radioDir, "login"), + loginFiles); + loadOggsFlat( + new File(radioDir, "reg"), + loginFiles); + } + + private void loadOggsFlat( + File dir, List list) { + if (!dir.exists() || !dir.isDirectory()) + return; + File[] files = dir.listFiles( + (d, n) -> n.toLowerCase() + .endsWith(".ogg")); + if (files == null) return; + Arrays.sort(files); + for (File f : files) list.add(f); + } + /** + * 解析 pack.png:优先读取插件目录, + * 没有则搜索图片并用ffmpeg转码 + */ + private File resolvePackIcon() { + File dataDir = plugin.getDataFolder(); + File packPng = new File(dataDir, "pack.png"); + + if (packPng.exists() + && packPng.length() > 0) { + plugin.getLogger().info( + "[Radio] 找到 pack.png (" + + packPng.length() + + " bytes)"); + return packPng; + } + + // 搜索可转换的图片文件 + String[] exts = {"png", "jpg", "jpeg", + "bmp", "webp", "tiff", "gif"}; + File[] found = dataDir.listFiles( + (dir, name) -> { + String low = name.toLowerCase(); + for (String ext : exts) { + if (low.endsWith("." + ext) + && !name.equals( + "pack.png")) { + return true; + } + } + return false; + }); + + if (found != null && found.length > 0) { + File src = found[0]; + plugin.getLogger().info( + "[Radio] 找到图片: " + + src.getName() + + " → 转码为 pack.png"); + if (convertToPackPng(src, packPng)) { + return packPng; + } + plugin.getLogger().warning( + "[Radio] 转码失败"); + return null; + } + + plugin.getLogger().warning( + "[Radio] 未找到 pack.png 或可转换图片"); + return null; + } + + /** + * ffmpeg 将任意图片转为 256x256 PNG + */ + private boolean convertToPackPng( + File input, File output) { + try { + ProcessBuilder pb = new ProcessBuilder( + "ffmpeg", "-y", + "-i", input.getAbsolutePath(), + "-vf", + "scale='min(256,iw)'" + + ":'min(256,ih)':" + + "force_original_aspect_ratio" + + "=decrease," + + "pad=256:256:" + + "(ow-iw)/2:" + + "(oh-ih)/2:" + + "color=0x00000000", + "-c:v", "png", + output.getAbsolutePath()); + pb.redirectErrorStream(true); + Process p = pb.start(); + boolean ok = p.waitFor( + 30, TimeUnit.SECONDS); + if (!ok) { + p.destroyForcibly(); + return false; + } + return output.exists() + && output.length() > 0; + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] ffmpeg转码失败: " + + e.getMessage()); + return false; + } + } + + // ========== 资源包构建 ========== + private void buildResourcePack() { + deleteDir(packDir); + packDir.mkdirs(); + + List allOggs = new ArrayList<>(); + collectOggs(radioDir, allOggs); + + if (allOggs.isEmpty()) { + plugin.getLogger().warning( + "[Radio] radio/ 目录下" + + "无 .ogg 文件,跳过资源包"); + return; + } + + // 正确路径:assets/minecraft/sounds/custom/ + File soundsDir = new File(packDir, + "assets/minecraft/sounds/custom"); + soundsDir.mkdirs(); + + // 生成 sounds.json + StringBuilder json = + new StringBuilder("{\n"); + for (int i = 0; i < allOggs.size(); i++) { + File ogg = allOggs.get(i); + String simpleName = + ogg.getName() + .replace(".ogg", ""); + File dest = new File(soundsDir, + simpleName + ".ogg"); + try { + Files.copy(ogg.toPath(), + dest.toPath(), + StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + // ignore + } + // 音频事件名: custom.xxx + // 文件路径: custom/xxx + json.append(" \"custom.") + .append(simpleName) + .append("\": {\n"); + json.append(" \"sounds\": [{\n"); + json.append(" \"name\":" + + " \"custom/") + .append(simpleName) + .append("\",\n"); + json.append( + " \"stream\": true\n"); + json.append(" }]\n"); + json.append(" }"); + if (i < allOggs.size() - 1) + json.append(","); + json.append("\n"); + } + json.append("}"); + + // sounds.json 放在 assets/minecraft/ 下 + try { + Files.write(new File(packDir, + "assets/minecraft/sounds.json") + .toPath(), + json.toString() + .getBytes("UTF-8")); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] sounds.json写入失败"); + } +// sounds.json 写入后... + +// ===== 新增:打包 pack.png 作为资源包图标 ===== + File iconPng = resolvePackIcon(); + if (iconPng != null && iconPng.exists()) { + try { + Files.copy(iconPng.toPath(), + new File(packDir, "pack.png") + .toPath(), + StandardCopyOption + .REPLACE_EXISTING); + plugin.getLogger().info( + "[Radio] pack.png 已加入资源包"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] pack.png复制失败: " + + e.getMessage()); + } + } + + // pack.mcmeta(pack_format=54) + try { + Files.write(new File(packDir, + "pack.mcmeta").toPath(), + ("{\n \"pack\": {\n" + + " \"pack_format\": 54,\n" + + " \"description\":" + + " \"§d§l草原探险服务器资源包,请勿拒绝资源包\"\n }\n}") + .getBytes("UTF-8")); + } catch (Exception e) { + // ignore + } + + try { + File zipFile = new File( + plugin.getDataFolder(), + "radiopack.zip"); + packZip(packDir, zipFile); + plugin.getLogger().info( + "[Radio] 资源包生成: " + + allOggs.size() + " 个音频"); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] zip打包失败: " + + e.getMessage()); + } + } + + private void deleteDir(File dir) { + if (!dir.exists()) return; + File[] files = dir.listFiles(); + if (files != null) { + for (File f : files) { + if (f.isDirectory()) deleteDir(f); + else f.delete(); + } + } + dir.delete(); + } + + private void collectOggs( + File dir, List list) { + File[] files = dir.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) + collectOggs(f, list); + else if (f.getName().endsWith(".ogg")) + list.add(f); + } + } + + private void packZip(File root, + File zipFile) throws Exception { + try (ZipOutputStream zos = + new ZipOutputStream( + new FileOutputStream( + zipFile))) { + zipDir(root, root, zos); + } + } + + private void zipDir(File root, + File current, + ZipOutputStream zos) + throws Exception { + File[] files = current.listFiles(); + if (files == null) return; + for (File f : files) { + if (f.isDirectory()) { + zipDir(root, f, zos); + } else { + String path = root.toURI() + .relativize(f.toURI()) + .getPath(); + zos.putNextEntry(new ZipEntry(path)); + zos.write(Files.readAllBytes( + f.toPath())); + zos.closeEntry(); + } + } + } + + // ========== 资源包发送 ========== + + /** + * 发送资源包(带限流,不禁重试) + */ + private final Map lastSendTime = + new ConcurrentHashMap<>(); + + public void sendResourcePack(Player p) { + UUID uuid = p.getUniqueId(); + + // 最短间隔 5秒(不是10秒的全锁死) + Long last = lastSendTime.get(uuid); + if (last != null + && System.currentTimeMillis() - last + < 5000L) { + plugin.getLogger().info( + "[Radio] 跳过(5秒内): " + + p.getName()); + return; + } + lastSendTime.put(uuid, + System.currentTimeMillis()); + + String url = getResourcePackUrl(); + if (url == null || url.isEmpty()) { + plugin.getLogger().warning( + "[Radio] URL为空,跳过: " + + p.getName()); + return; + } + + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) { + plugin.getLogger().warning( + "[Radio] radiopack.zip不存在," + + "跳过: " + p.getName()); + return; + } + + try { + byte[] data = + Files.readAllBytes(zip.toPath()); + String hash = sha1(data); + long fileSize = zip.length(); + + plugin.getLogger().info( + "[Radio] 发送: " + p.getName() + + " url=" + url + + " size=" + fileSize + + " hash=" + hash); + + sendPackWithHash(p, url, hash); + + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] 发送异常: " + + p.getName()); + e.printStackTrace(); + } + } + + + + private void sendPackWithHash( + Player p, + String url, + String hash) { + + UUID uuid = UUID.fromString( + "a1b2c3d4-e5f6-7890-abcd-" + + "ef1234567890"); + + // 尝试5参 + Component + try { + Object component = + net.kyori.adventure.text + .Component.text( + "§d§l请下载自定义资源包"); + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + net.kyori.adventure + .text.Component + .class, + boolean.class) + .invoke( + p, uuid, url, + hash, component, + true); + plugin.getLogger().info( + "[Radio] 成功(5参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试5参 + String描述 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class, + String.class, + boolean.class) + .invoke( + p, uuid, url, + hash, + "Sdf1 Radio", + true); + plugin.getLogger().info( + "[Radio] 成功(5参String): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // 尝试3参 + try { + p.getClass().getMethod( + "setResourcePack", + UUID.class, + String.class, + String.class) + .invoke( + p, uuid, url, + hash); + plugin.getLogger().info( + "[Radio] 成功(3参): " + + p.getName()); + return; + } catch (Exception ignored) { + } + + // ★ 全部失败 → 通知玩家 + 熔断 + plugin.getLogger().severe( + "[Radio] 所有签名失败: " + + p.getName() + + " 客户端版本: " + + p.getProtocolVersion()); + p.sendMessage( + "§e§l[Radio] §f资源包发送失败," + + "§7部分功能可能异常"); + p.sendMessage( + "§7可尝试: §e/rejoin §7或重启客户端"); + } + + + + private void sendPackFromLocal( + Player p, String url) { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (!zip.exists()) return; + try { + byte[] data = + Files.readAllBytes( + zip.toPath()); + String hash = sha1(data); + sendPackWithHash(p, url, hash); + } catch (Exception e) { + plugin.getLogger().warning( + "[Radio] 降级发送失败"); + } + } + + // 从URL下载字节 + private byte[] downloadBytes(String urlStr) + throws Exception { + java.net.URL url = + new java.net.URL(urlStr); + java.net.HttpURLConnection conn = + (java.net.HttpURLConnection) + url.openConnection(); + conn.setConnectTimeout(10000); + conn.setReadTimeout(30000); + try (java.io.InputStream is = + conn.getInputStream()) { + return is.readAllBytes(); + } + } + + // SHA-1计算 + private String sha1(byte[] data) + throws Exception { + java.security.MessageDigest md = + java.security.MessageDigest + .getInstance("SHA-1"); + md.update(data); + byte[] hash = md.digest(); + StringBuilder sb = + new StringBuilder(); + for (byte b : hash) + sb.append(String.format( + "%02x", b)); + return sb.toString(); + } + + public void clearPending(UUID uuid) { + lastSendTime.remove(uuid); + } + + + + // ========== 内嵌HTTP服务 ========== + private com.sun.net.httpserver.HttpServer + httpServer; + + private void startHttpServer() { + // 如果配置了外部地址则不启动 + if (plugin.getConfig2() != null + && !plugin.getConfig2() + .broadcastServerUrl + .isEmpty()) { + return; + } + try { + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + httpServer = + com.sun.net.httpserver + .HttpServer.create( + new java.net + .InetSocketAddress( + port), 0); + httpServer.createContext( + "/radiopack.zip", + exchange -> { + File zip = new File( + plugin.getDataFolder(), + "radiopack.zip"); + if (zip.exists()) { + byte[] data = + Files.readAllBytes( + zip.toPath()); + exchange.getResponseHeaders() + .set("Content-Type", + "application/zip"); + exchange.getResponseHeaders() + .set("Access-Control" + + "-Allow-Origin", + "*"); + exchange.sendResponseHeaders( + 200, data.length); + exchange.getResponseBody() + .write(data); + exchange.getResponseBody() + .close(); + } else { + byte[] empty = + "not found".getBytes(); + exchange.sendResponseHeaders( + 404, empty.length); + exchange.getResponseBody() + .write(empty); + exchange.getResponseBody() + .close(); + } + }); + httpServer.setExecutor(null); + httpServer.start(); + plugin.getLogger().info( + "[Radio] HTTP服务: http://0.0.0.0:" + + port + "/radiopack.zip"); + } catch (Exception e) { + plugin.getLogger().severe( + "[Radio] HTTP启动失败: " + + e.getMessage()); + } + } + + private void stopHttpServer() { + if (httpServer != null) { + httpServer.stop(0); + httpServer = null; + } + } + + public String getResourcePackUrl() { + // 有外部配置 → 随机生成URL + if (externalUrl != null + && !externalUrl.trim().isEmpty()) { + return generateRandomUrl(externalUrl); + } + // 无配置 → 用内置HTTP + int port = 8080; + if (plugin.getConfig2() != null) { + port = plugin.getConfig2().httpPort; + } + return "http://127.0.0.1:" + + port + "/radiopack.zip"; + } + + + /** + * 从配置的域名列表中随机选一个 + * 生成随机、带时间戳的完整资源包URL + * + * 规则: + * *.example.com + * → 随机子域+时间戳 + * 例: a3f7k2.example.com/pack.zip?t=xxx&v=b9c1e4 + * + * cdn.example.com (无*) + * → 直接使用+时间戳 + * 例: cdn.example.com/pack.zip?t=xxx + */ + private String generateRandomUrl( + String raw) { + String[] parts = raw.split("\\|"); + String pick = + parts[(int)(Math.random() + * parts.length)].trim(); + + // 去掉协议 https:// http:// + String cleaned = pick + .replaceFirst("^https?://", ""); + // 去掉路径 /path/a?v=1 + if (cleaned.contains("/")) { + cleaned = cleaned.substring( + 0, cleaned.indexOf('/')); + } + + // 随机子域 + String hex = Long.toHexString( + Double.doubleToLongBits( + Math.random())); + String rand = hex.length() > 6 + ? hex.substring(2, 8) : hex; + rand = rand.replaceAll( + "[^a-z0-9]", "x"); + + String host; + if (cleaned.startsWith("*")) { + host = rand + + cleaned.substring(1); + } else { + host = cleaned; + } + + String ts = String.valueOf( + System.currentTimeMillis()); + String token = Integer.toHexString( + (int)(Math.random() + * 0xFFFFFF)); + + String url = "https://" + host + + "/radiopack.zip" + + "?t=" + ts + + "&v=" + token; + + plugin.getLogger().info( + "[Radio] 生成URL: " + + pick + " → " + url); + return url; + } + + + + // ========== 全服广播 ========== + public void startMainRadio() { + stopMainRadio(); + if (mainFiles.isEmpty()) { + plugin.getLogger().warning( + "[Radio] 无 .ogg 文件"); + return; + } + plugin.getLogger().info( + "[Radio] 启动全服广播 (" + + mainFiles.size() + " 个文件)"); + mainRadioTask = new BukkitRunnable() { + @Override + public void run() { + playNext(); + } + }.runTaskTimer(plugin, 100L, 2400L); + } + + private void playNext() { + if (mainFiles.isEmpty()) return; + File file = mainFiles.get( + radioIndex % mainFiles.size()); + String name = file.getName() + .replace(".ogg", ""); + lastPlayedSound = "custom." + name; + String key = "minecraft:" + lastPlayedSound; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + radioIndex++; + } + + // ========== 按名称播放 ========== + public void playToAll(String name) { + String key = findSongKey(name); + if (key == null) return; + lastPlayedSound = "custom." + name; + for (Player p : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + name); + } + + + public void playToPlayer( + Player p, String name) { + String key = findSongKey(name); + if (key == null) { + p.sendMessage("§c未找到: " + name); + return; + } + p.playSound(p.getLocation(), key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + + public void playToLoginPlayers(String name) { + String key = findSongKey(name); + if (key == null) return; + for (String pName + : loginPlayers.keySet()) { + Player lp = Bukkit.getPlayer(pName); + if (lp != null && lp.isOnline()) { + lp.playSound(lp.getLocation(), + key, + SoundCategory.RECORDS, + 1.5f, 1.0f); + } + } + } + + + private String findSongKey(String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + return "minecraft:custom." + name; + } + } + plugin.getLogger().warning( + "[Radio] 未找到歌曲: " + name); + return null; + } + + + public boolean hasSong(String name) { + return findSongKey(name) != null; + } + + // ========== 手动播放 ========== + public boolean playSpecific( + Player sender, String name) { + List all = new ArrayList<>(); + collectOggs(radioDir, all); + for (File f : all) { + String fn = f.getName() + .replace(".ogg", ""); + if (fn.equalsIgnoreCase(name)) { + String key = "sdf1radio:" + name; + for (Player p + : Bukkit.getOnlinePlayers()) { + p.playSound(p.getLocation(), + key, + SoundCategory.RECORDS, + 2.0f, 1.0f); + } + Bukkit.broadcastMessage( + "§6§l[广播] §f正在播放: §e" + + name); + return true; + } + } + if (sender != null) + sender.sendMessage("§c未找到: " + name); + return false; + } + public void stopMainRadio() { + if (mainRadioTask != null) { + mainRadioTask.cancel(); + mainRadioTask = null; + } + if (lastPlayedSound != null + && !lastPlayedSound.isEmpty()) { + for (Player p : Bukkit.getOnlinePlayers()) { + p.stopSound(lastPlayedSound, + SoundCategory.RECORDS); + } + lastPlayedSound = ""; + } + } + +// 在 init() 或 registerEvents 中保存引用: +// this.listener = new RadioDownloadListener(plugin); +private RadioDownloadListener radioListener; + + + + // ========== 重载 ========== + public void reload() { + stopMainRadio(); + stopAllLoginRadio(); + stopHttpServer(); + loadAllOggs(); + buildResourcePack(); + startHttpServer(); + loadRadioConfig(); + } +} \ No newline at end of file diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/RefundRecord.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RefundRecord.java new file mode 100644 index 0000000..c0a3070 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RefundRecord.java @@ -0,0 +1,62 @@ +package Sdf1_login; + +import org.bukkit.Material; +import java.util.UUID; + +public class RefundRecord { + public final UUID playerUUID; + public final String playerName; + public final String itemId; + public final String itemName; + public final Material material; + public final Material shulkerMaterial; + public int amount; + public int totalPaid; + public final long timestamp; + + // 普通购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, int amount, + int totalPaid, long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = null; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + // 盒装购买 + public RefundRecord(UUID uuid, String name, + String itemId, String itemName, + Material material, + Material shulkerMat, + int amount, int totalPaid, + long timestamp) { + this.playerUUID = uuid; + this.playerName = name; + this.itemId = itemId; + this.itemName = itemName; + this.material = material; + this.shulkerMaterial = shulkerMat; + this.amount = amount; + this.totalPaid = totalPaid; + this.timestamp = timestamp; + } + + public boolean isExpired() { + return System.currentTimeMillis() + - timestamp > 300000L; + } + + public int getSecondsLeft() { + long left = 300000L + - (System.currentTimeMillis() + - timestamp); + return (int) Math.max(0, left / 1000); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/RiskControlManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RiskControlManager.java new file mode 100644 index 0000000..4ddedcc --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/RiskControlManager.java @@ -0,0 +1,248 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 风控管理器:检测异常登录行为,记录尝试次数,触发安全报警 + */ +public class RiskControlManager { + + private final Main plugin; + + // 每个玩家名 -> 连续失败次数 + private final Map failAttempts = + new ConcurrentHashMap<>(); + + // 每个IP -> 关联的可疑玩家列表 + private final Map> ipSuspicious = + new ConcurrentHashMap<>(); + + // 每个玩家 -> 上次验证时间(用于冷却) + private final Map verifyCooldown = + new ConcurrentHashMap<>(); + + // 配置项 + private static final int MAX_FAIL_ATTEMPTS = 5; + private static final int MAX_ACCOUNTS_PER_IP = 5; + private static final long COOLDOWN_MS = 60000L; + + public RiskControlManager(Main plugin) { + this.plugin = plugin; + } + + /** + * 记录一次失败的登录尝试 + * + * @param playerName 玩家名 + * @return 剩余尝试次数 + */ + public int recordFailAttempt(String playerName) { + int current = failAttempts + .getOrDefault(playerName, 0) + 1; + failAttempts.put(playerName, current); + return Math.max(0, MAX_FAIL_ATTEMPTS - current); + } + + /** + * 重置指定玩家的失败计数(登录成功时调用) + */ + public void resetFailAttempts(String playerName) { + failAttempts.remove(playerName); + } + + /** + * 获取指定玩家的失败次数 + */ + public int getFailAttempts(String playerName) { + return failAttempts + .getOrDefault(playerName, 0); + } + + /** + * 判断玩家是否已被锁定(失败次数达到上限) + */ + public boolean isLocked(String playerName) { + return failAttempts + .getOrDefault(playerName, 0) + >= MAX_FAIL_ATTEMPTS; + } + + /** + * 检查IP是否达到注册上限 + * 直接查数据库,不依赖 DatabaseManager 的方法 + */ + public boolean canRegisterFromIP(String ip) { + if (ip == null || ip.isEmpty()) return true; + try { + java.sql.Connection conn = + plugin.getDb().getDb(); + if (conn == null) return true; + java.sql.PreparedStatement ps = + conn.prepareStatement( + "SELECT COUNT(*) FROM users " + + "WHERE ip_address = ?"); + ps.setString(1, ip); + java.sql.ResultSet rs = ps.executeQuery(); + int count = 0; + if (rs.next()) count = rs.getInt(1); + rs.close(); + ps.close(); + return count < 5; + } catch (Exception e) { + e.printStackTrace(); + return true; + } + } + + + /** + * 记录IP关联的可疑行为 + */ + public void recordSuspiciousIP(String ip, + String playerName) { + if (ip == null || ip.isEmpty()) return; + ipSuspicious + .computeIfAbsent(ip, + k -> ConcurrentHashMap.newKeySet()) + .add(playerName); + } + + /** + * 获取某IP关联的所有可疑玩家 + */ + public Set getSuspiciousAccounts(String ip) { + return ipSuspicious + .getOrDefault(ip, Collections.emptySet()); + } + + /** + * 检查是否在验证冷却期内 + */ + public boolean isOnCooldown(String playerName) { + Long last = verifyCooldown.get(playerName); + if (last == null) return false; + return System.currentTimeMillis() - last + < COOLDOWN_MS; + } + + /** + * 设置验证冷却时间 + */ + public void setCooldown(String playerName) { + verifyCooldown.put(playerName, + System.currentTimeMillis()); + } + + /** + * 当检测到可疑登录时触发报警 + * - 全服广播 + * - 有邮箱则发邮件 + * - 记录到数据库 + */ + public void triggerAlert(Player player, + String suspiciousIP) { + String name = player.getName(); + + // 记录到数据库 + plugin.getDb().recordSecurityAlert( + name, suspiciousIP); + + // 记录IP可疑行为 + recordSuspiciousIP(suspiciousIP, name); + + // 全服广播 + String serverMsg = "§c§l[安全警告] §e" + name + + " §f的账号疑似遭到 §c" + suspiciousIP + + " §f的盗号尝试, 请各位玩家注意账号安全!"; + Bukkit.broadcastMessage(serverMsg); + + // 有邮箱则发邮件 + String emailAddr = (String) plugin.getDb() + .getField(name, "email"); + if (emailAddr != null + && !emailAddr.isEmpty()) { + String subject = + "[安全警告] 您的账号疑似遭到盗号尝试"; + String body = "尊敬的玩家 " + name + + ":\n\n" + + "您的账号在 " + + new java.text.SimpleDateFormat( + "yyyy-MM-dd HH:mm:ss") + .format(new java.util.Date()) + + " 疑似遭到 IP: " + suspiciousIP + + " 的盗号尝试。\n\n" + + "建议您:\n" + + "1. 立即修改密码" + + "(使用 /sdf1_login pw)\n" + + "2. 如非本人操作," + + "请联系管理员处理\n\n" + + "--Sdf1_login 安全系统"; + plugin.getEmail().sendTempPassword( + emailAddr, name, body); + } + } + + /** + * 处理登录失败时的风控逻辑 + * + * @param player 玩家 + * @param suspiciousIP 可疑来源IP + * @return true=已被锁定(应踢出), false=仍可继续尝试 + */ + public boolean handleLoginFailure(Player player, + String suspiciousIP) { + String name = player.getName(); + int remaining = recordFailAttempt(name); + + if (remaining <= 0) { + // 达到上限,触发报警 + triggerAlert(player, suspiciousIP); + player.kickPlayer( + "§c§l[风控] §f登录失败次数过多," + + " 账号已被临时锁定, 请联系管理员"); + return true; + } + + // 未达上限,提示剩余次数 + player.sendMessage( + "§c§l[风控] §f密码错误! " + + "剩余" + remaining + "次机会"); + return false; + } + + /** + * 登录成功后重置风控状态 + */ + public void onLoginSuccess(String playerName) { + resetFailAttempts(playerName); + } + + /** + * 清理过期的冷却记录(可由定时任务调用) + */ + public void cleanup() { + long now = System.currentTimeMillis(); + verifyCooldown.entrySet().removeIf( + e -> now - e.getValue() > COOLDOWN_MS * 10); + } + + /** + * 获取所有玩家的失败尝试记录(调试用) + */ + public Map getAllFailAttempts() { + return Collections.unmodifiableMap(failAttempts); + } + + /** + * 清除所有风控数据(管理员调试用) + */ + public void clearAll() { + failAttempts.clear(); + ipSuspicious.clear(); + verifyCooldown.clear(); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/SalesStatsManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/SalesStatsManager.java new file mode 100644 index 0000000..9d06a22 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/SalesStatsManager.java @@ -0,0 +1,170 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class SalesStatsManager { + + private final Main plugin; + + private final Map sessionSales = + new ConcurrentHashMap<>(); + private int sessionRevenue = 0; + private int sessionOrders = 0; + private int sessionItems = 0; + private int sessionRefunds = 0; + private int sessionRefundAmount = 0; + private long sessionStart = System.currentTimeMillis(); + + // 状态 + private boolean waitingForEmpty = false; + private boolean hasReported = false; + private BukkitRunnable waitTask = null; + + public SalesStatsManager(Main plugin) { + this.plugin = plugin; + startMonitor(); + } + + // ★ 每10秒检测一次 + private void startMonitor() { + new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 有玩家在线 → 重置等待状态 + if (waitingForEmpty) { + // 刚有人进来了,清除已汇报标记 + // 下次没人时会重新汇报 + } + waitingForEmpty = false; + hasReported = false; + cancelWaitTask(); + } else if (!waitingForEmpty && !hasReported) { + // 没人了,且还没汇报过 → 开始1分钟倒计时 + waitingForEmpty = true; + startWaitCountdown(); + } + } + }.runTaskTimer(plugin, 200L, 200L); + } + + // ★ 1分钟等待 + private void startWaitCountdown() { + cancelWaitTask(); + waitTask = new BukkitRunnable() { + @Override + public void run() { + int online = Bukkit.getOnlinePlayers().size(); + + if (online > 0) { + // 等待期间有人进来 → 取消,等下次没人 + waitingForEmpty = false; + hasReported = false; + waitTask = null; + cancel(); + return; + } + + // 1分钟没人 → 汇报一次 + if (!hasReported) { + hasReported = true; + report(); + } + waitingForEmpty = false; + waitTask = null; + cancel(); + } + }; + waitTask.runTaskLater(plugin, 1200L); // 60秒 + } + + private void cancelWaitTask() { + if (waitTask != null) { + waitTask.cancel(); + waitTask = null; + } + } + + // ===== 记录 ===== + + public void recordSale(String itemName, int price) { + sessionSales.merge(itemName, 1, Integer::sum); + sessionRevenue += price; + sessionOrders++; + sessionItems++; + } + + public void recordRefund(int amount) { + sessionRefunds++; + sessionRefundAmount += amount; + } + + // ===== 汇报 ===== + + public void report() { + if (sessionItems == 0 && sessionRefunds == 0) { + // plugin.getLogger().info("[Sales] 无数据,跳过"); + return; + } + + long minutes = + (System.currentTimeMillis() - sessionStart) + / 60000; + if (minutes < 1) minutes = 1; + + StringBuilder sb = new StringBuilder(); + sb.append("§6§l========== 销量汇总 =========="); + sb.append("\n§7时长: §f").append(minutes).append("分钟"); + sb.append("\n§7订单: §f").append(sessionOrders).append("笔"); + sb.append("\n§7商品: §f").append(sessionItems).append("件"); + sb.append("\n§7成交额: §6").append(sessionRevenue).append("枚"); + sb.append("\n§7退款: §c").append(sessionRefunds) + .append("次(§c").append(sessionRefundAmount) + .append("枚§7)"); + sb.append("\n§6§l------------------------------"); + + if (!sessionSales.isEmpty()) { + sb.append("\n§7销量排行:"); + List> sorted = + new ArrayList<>(sessionSales.entrySet()); + sorted.sort((a, b) -> + b.getValue() - a.getValue()); + int rank = 1; + for (Map.Entry e : sorted) { + if (rank > 10) break; + sb.append("\n§e").append(rank).append(". ") + .append(e.getKey()) + .append(" §7x").append(e.getValue()); + rank++; + } + } + sb.append("\n§6§l=============================="); + + String msg = sb.toString(); + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.isOp()) { + for (String line : msg.split("\n")) + p.sendMessage(line); + } + } + plugin.getLogger().info("[Sales] 汇报完成"); + resetSession(); + } + + public void resetSession() { + sessionSales.clear(); + sessionRevenue = 0; + sessionOrders = 0; + sessionItems = 0; + sessionRefunds = 0; + sessionRefundAmount = 0; + sessionStart = System.currentTimeMillis(); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopCategory.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopCategory.java new file mode 100644 index 0000000..715e9ad --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopCategory.java @@ -0,0 +1,32 @@ +package Sdf1_login; + +import java.util.ArrayList; +import java.util.List; + +public class ShopCategory { + private final String name; + private final String fileName; + private final List items; + + public ShopCategory(String name, String fileName) { + this.name = name; + this.fileName = fileName; + this.items = new ArrayList<>(); + } + + public String getName() { return name; } + public String getFileName() { return fileName; } + public List getItems() { return items; } + + public ShopItem getItem(String id) { + for (ShopItem item : items) { + if (item.getId().equalsIgnoreCase(id)) + return item; + } + return null; + } + + public void addItem(ShopItem item) { + items.add(item); + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopItem.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopItem.java new file mode 100644 index 0000000..d336c7c --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopItem.java @@ -0,0 +1,49 @@ +package Sdf1_login; + +import org.bukkit.Material; + +public class ShopItem { + private final String id; + private final String displayName; + private final Material material; + private final int buyPrice; + private final int sellPrice; + private int stock; + private int hourlySales; + private int totalSales; + public ShopItem(String id, String displayName, + Material material, int buyPrice, + int sellPrice, int stock, + int hourlySales, int totalSales) { + this.id = id; + this.displayName = displayName; + this.material = material; + this.buyPrice = buyPrice; + this.sellPrice = sellPrice; + this.stock = stock; + this.hourlySales = hourlySales; + this.totalSales = totalSales; + } + + public int getEffectiveSellPrice() { + if (sellPrice <= 0) return 0; + return sellPrice; + } + + public String getId() { return id; } + public String getDisplayName() { return displayName; } + public Material getMaterial() { return material; } + public int getBuyPrice() { return buyPrice; } + public int getSellPrice() { return sellPrice; } + public int getStock() { return stock; } + public int getHourlySales() { return hourlySales; } + public int getTotalSales() { return totalSales; } + + public void setStock(int s) { this.stock = s; } + public void setHourlySales(int s) { this.hourlySales = s; } + public void setTotalSales(int s) { this.totalSales = s; } + public void addSales(int n) { + this.hourlySales += n; + this.totalSales += n; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopManager.java new file mode 100644 index 0000000..cdcc045 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/ShopManager.java @@ -0,0 +1,2610 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.Base64; +import org.bukkit.util.io.BukkitObjectInputStream; +import org.bukkit.util.io.BukkitObjectOutputStream; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.inventory.meta.PotionMeta; + + + + +public class ShopManager implements Listener { + + private final Main plugin; + private final List categories = + new ArrayList<>(); + private final List refundRecords = + new ArrayList<>(); + private final Map activeDiscount = + new HashMap<>(); + private final Map viewCat = + new HashMap<>(); + private final Map viewPage = + new HashMap<>(); + private final Map> shoppingCarts = + new HashMap<>(); + private final Set cartModeEnabled = + new HashSet<>(); + private final java.util.Random rng = + new java.util.Random(); + private final Map> pendingCheckout = + new HashMap<>(); + private final Set couponListening = + new HashSet<>(); + private final Map pendingCouponCode = + new HashMap<>(); + // ===== 物品NBT数据 ===== + private final Map itemNbtData = + new HashMap<>(); + private final Map itemStackCache = + new HashMap<>(); + // 替换字段 + private final Map couponApplyTime = new HashMap<>(); + + + + private static final Material[] SHULKER_COLORS = { + Material.SHULKER_BOX, + Material.WHITE_SHULKER_BOX, + Material.ORANGE_SHULKER_BOX, + Material.MAGENTA_SHULKER_BOX, + Material.LIGHT_BLUE_SHULKER_BOX, + Material.YELLOW_SHULKER_BOX, + Material.LIME_SHULKER_BOX, + Material.PINK_SHULKER_BOX, + Material.GRAY_SHULKER_BOX, + Material.LIGHT_GRAY_SHULKER_BOX, + Material.CYAN_SHULKER_BOX, + Material.PURPLE_SHULKER_BOX, + Material.BLUE_SHULKER_BOX, + Material.BROWN_SHULKER_BOX, + Material.GREEN_SHULKER_BOX, + Material.RED_SHULKER_BOX, + Material.BLACK_SHULKER_BOX + }; + private static final Map EFFECT_MAP = new HashMap<>(); + static { + // ===== 基础效果 ===== + EFFECT_MAP.put("夜视", PotionEffectType.NIGHT_VISION); + EFFECT_MAP.put("隐身", PotionEffectType.INVISIBILITY); + EFFECT_MAP.put("迅捷", PotionEffectType.SPEED); + EFFECT_MAP.put("速度", PotionEffectType.SPEED); + EFFECT_MAP.put("力量", PotionEffectType.STRENGTH); // 1.21+ 改名 + EFFECT_MAP.put("治疗", PotionEffectType.INSTANT_HEALTH); // 1.21+ 改名 + EFFECT_MAP.put("治愈", PotionEffectType.INSTANT_HEALTH); + EFFECT_MAP.put("伤害", PotionEffectType.INSTANT_DAMAGE); // 1.21+ 改名 + EFFECT_MAP.put("剧毒", PotionEffectType.POISON); + EFFECT_MAP.put("中毒", PotionEffectType.POISON); + EFFECT_MAP.put("回复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("再生", PotionEffectType.REGENERATION); + EFFECT_MAP.put("生命恢复", PotionEffectType.REGENERATION); + EFFECT_MAP.put("抗性", PotionEffectType.RESISTANCE); // 1.21+ 改名 + EFFECT_MAP.put("抗火", PotionEffectType.FIRE_RESISTANCE); + EFFECT_MAP.put("水下呼吸", PotionEffectType.WATER_BREATHING); + EFFECT_MAP.put("急迫", PotionEffectType.HASTE); // 1.21+ 改名 + EFFECT_MAP.put("挖掘疲劳", PotionEffectType.MINING_FATIGUE); // 1.21+ 改名 + EFFECT_MAP.put("虚弱", PotionEffectType.WEAKNESS); + EFFECT_MAP.put("失明", PotionEffectType.BLINDNESS); + EFFECT_MAP.put("飘浮", PotionEffectType.LEVITATION); + EFFECT_MAP.put("缓降", PotionEffectType.SLOW_FALLING); // ★ 补充 + EFFECT_MAP.put("迟缓", PotionEffectType.SLOWNESS); + EFFECT_MAP.put("饱和", PotionEffectType.SATURATION); + EFFECT_MAP.put("伤害吸收", PotionEffectType.ABSORPTION); + EFFECT_MAP.put("幸运", PotionEffectType.LUCK); + EFFECT_MAP.put("不祥", PotionEffectType.UNLUCK); + EFFECT_MAP.put("发光", PotionEffectType.GLOWING); + EFFECT_MAP.put("黑暗", PotionEffectType.DARKNESS); + EFFECT_MAP.put("生命加成", PotionEffectType.HEALTH_BOOST); + EFFECT_MAP.put("饥饿", PotionEffectType.HUNGER); + EFFECT_MAP.put("反胃", PotionEffectType.NAUSEA); + EFFECT_MAP.put("海豚关怀", PotionEffectType.DOLPHINS_GRACE); + EFFECT_MAP.put("跳跃", PotionEffectType.JUMP_BOOST); + // ★ 神龟药水是双效果,特殊处理,这里只做名称匹配入口 + EFFECT_MAP.put("神龟", PotionEffectType.RESISTANCE); + } + + public static class CartItem { + String itemId; + String displayName; + Material material; + int unitPrice; + int quantity; + } + + + private static final int PAGE_SIZE = 28; + private static final int[] ITEM_SLOTS = { + 10,11,12,13,14,15,16, + 19,20,21,22,23,24,25, + 28,29,30,31,32,33,34, + 37,38,39,40,41,42,43 + }; + + public ShopManager(Main plugin) { + this.plugin = plugin; + loadCategories(); + startHourlyTask(); + loadItemNbtData(); + startCleanupTask(); + } + // ===== 切换购物车模式 ===== + // ===== 切换购物车模式 ===== + public boolean toggleCartMode(Player p) { + UUID uuid = p.getUniqueId(); + if (cartModeEnabled.contains(uuid)) { + cartModeEnabled.remove(uuid); + p.sendMessage("§7购物车模式已关闭,恢复正常购买"); + return false; + } else { + cartModeEnabled.add(uuid); + p.sendMessage("§a购物车模式已开启,点击商品加入购物车"); + return true; + } + } + + public boolean isCartMode(Player p) { + return cartModeEnabled.contains(p.getUniqueId()); + } + private String itemToBase64(ItemStack item) { + try { + ByteArrayOutputStream os = + new ByteArrayOutputStream(); + BukkitObjectOutputStream bos = + new BukkitObjectOutputStream(os); + bos.writeObject(item); + bos.close(); + return Base64.getEncoder() + .encodeToString(os.toByteArray()); + } catch (Exception e) { + return ""; + } + } + + private ItemStack base64ToItem(String b64) { + try { + byte[] data = + Base64.getDecoder().decode(b64); + ByteArrayInputStream is = + new ByteArrayInputStream(data); + BukkitObjectInputStream bis = + new BukkitObjectInputStream(is); + ItemStack item = + (ItemStack) bis.readObject(); + bis.close(); + return item; + } catch (Exception e) { + return null; + } + } + + private void loadItemNbtData() { + itemNbtData.clear(); + itemStackCache.clear(); + File f = new File(plugin.getDataFolder(), + "shop/item_nbt.properties"); + if (!f.exists()) return; + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.isEmpty() + || line.startsWith("#")) continue; + int eq = line.indexOf('='); + if (eq < 0) continue; + String id = + line.substring(0, eq).trim(); + String b64 = + line.substring(eq + 1).trim(); + itemNbtData.put(id, b64); + ItemStack stack = base64ToItem(b64); + if (stack != null) { + itemStackCache.put(id, stack); + } + } + br.close(); + // plugin.getLogger().info("[Shop] NBT数据: " + // + itemStackCache.size() + "条"); + } catch (Exception ignored) {} + } + + private void saveItemNbtData() { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, "item_nbt.properties"); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# 商品完整数据(自动生成)"); + for (Map.Entry e + : itemNbtData.entrySet()) { + pw.println(e.getKey() + "=" + + e.getValue()); + } + pw.flush(); + pw.close(); + } catch (Exception ignored) {} + } + + // ===== 加入/移除购物车 ===== + public boolean addToCart(Player p, ShopItem item) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts + .computeIfAbsent( + uuid, k -> new ArrayList<>()); + // ★ 始终用原价,折扣在结算时统一应用 + int price = item.getBuyPrice(); + for (CartItem ci : cart) { + if (ci.itemId.equals(item.getId())) { + ci.quantity += 1; + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + } + CartItem ci = new CartItem(); + ci.itemId = item.getId(); + ci.displayName = item.getDisplayName(); + ci.material = item.getMaterial(); + ci.unitPrice = price; + ci.quantity = 1; + cart.add(ci); + p.sendMessage("§a+1 §e" + + item.getDisplayName() + + " §7购物车共§f" + + getCartCount(p) + + "件 §6需" + + getCartTotal(p) + "枚"); + return true; + } + + + public int getCartCount(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int c = 0; + for (CartItem ci : cart) c += ci.quantity; + return c; + } + + public int getCartTotal(Player p) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null) return 0; + int t = 0; + for (CartItem ci : cart) + t += ci.unitPrice * ci.quantity; + return t; + } + + public void cartAddOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + cart.get(index).quantity += 1; + } + + public void cartRemoveOne(Player p, int index) { + List cart = + shoppingCarts.get(p.getUniqueId()); + if (cart == null || index < 0 + || index >= cart.size()) return; + CartItem ci = cart.get(index); + ci.quantity -= 1; + if (ci.quantity <= 0) cart.remove(index); + } + + public void clearCart(Player p) { + shoppingCarts.remove(p.getUniqueId()); + } + + // ===== 打开购物车页面 ===== + public void openCart(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§a§l购物车"); + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + for (int i = 0; i < cart.size() && i < 25; i++) { + CartItem ci = cart.get(i); + ItemStack is = new ItemStack(ci.material); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + ci.displayName + + " §7x" + ci.quantity); + im.setLore(java.util.Arrays.asList( + "§7单价: §f" + ci.unitPrice + "枚", + "§7小计: §6" + + (ci.unitPrice * ci.quantity) + + "枚", + "", + "§a左键+1 §c右键-1")); + is.setItemMeta(im); + } + g.setItem(10 + i, is); + } + if (cart.isEmpty()) { + g.setItem(22, mkItem(Material.BARRIER, + "§7购物车为空")); + } + int total = getCartTotal(p); + int bal = plugin.getBonds().getBonds(p.getName()); + g.setItem(49, mkItem(Material.EMERALD, + "§6§l结算: §e" + total + " §6枚债券", + "§7余额: §f" + bal + "枚", + bal >= total ? "§a余额充足" : "§c余额不足", + "", "§a点击结算")); + g.setItem(48, mkItem( + Material.LIME_STAINED_GLASS_PANE, + "§7清空购物车")); + g.setItem(53, mkItem(Material.ARROW, "§7返回商店")); + + p.openInventory(g); + } + + // ===== 购物车点击处理 ===== + public boolean handleCartClick(Player p, int raw, + boolean left, + boolean right) { + if (raw == 53) { openShopMain(p); return true; } + if (raw == 48) { + clearCart(p); + p.sendMessage("§7购物车已清空"); + openCart(p); + return true; + } + if (raw == 49) { + checkoutCartInit(p); + return true; + } + if (raw >= 10 && raw < 35) { + int idx = raw - 10; + List cart = shoppingCarts + .getOrDefault(p.getUniqueId(), + new ArrayList<>()); + if (idx < cart.size()) { + if (right) cartRemoveOne(p, idx); + else cartAddOne(p, idx); + openCart(p); + } + return true; + } + return true; + } + + // ===== 结算入口:判断打包方式 ===== + private void checkoutCartInit(Player p) { + UUID uuid = p.getUniqueId(); + List cart = shoppingCarts.get(uuid); + if (cart == null || cart.isEmpty()) { + p.sendMessage("§c购物车为空"); + return; + } + int total = getCartTotal(p); + int bal = plugin.getBonds() + .getBonds(p.getName()); + if (bal < total) { + p.sendMessage("§c余额不足,需要 §e" + + total + "§c枚"); + return; + } + int totalQty = 0; + for (CartItem ci : cart) totalQty += ci.quantity; + if (cart.size() == 1 && totalQty <= 64) { + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + deductAndGive(p, cart, + (int) Math.round(total * factor), + false, null, "", 0, + ecoPct, couponPct); + return; + } + pendingCheckout.put(uuid, + new ArrayList<>(cart)); + openPackingConfirm(p, total); + } + + + + // ===== 执行扣款+发货 ===== + private boolean deductAndGive(Player p, + List cart, int baseTotal, + boolean useShulker, Material color, + String packNote, int packFee, + int ecoPct, int couponPct) { + + UUID uuid = p.getUniqueId(); + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + String[] names = new String[cart.size()]; + int[] subs = new int[cart.size()]; + for (int i = 0; i < cart.size(); i++) { + CartItem ci = cart.get(i); + names[i] = ci.displayName; + subs[i] = ci.unitPrice * ci.quantity; + } + + if (baseTotal < originalTotal) { + double ratio = + (double) baseTotal / originalTotal; + int sum = 0; + for (int i = 0; i < subs.length; i++) { + if (i == subs.length - 1) { + subs[i] = baseTotal - sum; + } else { + subs[i] = (int) (subs[i] * ratio); + sum += subs[i]; + } + } + } + + boolean ok = plugin.getBonds().checkoutCart( + p.getName(), names, subs); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + if (packFee > 0) { + plugin.getBonds().deductBonds( + p.getName(), packFee, + "shop_packing", "", + "商店系统", "打包费"); + } + + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + if (si != null) { + if (si.getStock() >= 0) + si.setStock(si.getStock() + - ci.quantity); + si.addSales(ci.quantity); + } + } + + int newBal = plugin.getBonds() + .getBonds(p.getName()); + + + // ===== 优惠券码 ===== + String usedCouponCode = + pendingCouponCode.get(uuid); + + // ===== 记录订单 ===== + OrderManager.OrderRecord latestOrder = null; + try { + List orderItems = + new ArrayList<>(); + for (CartItem ci : cart) { + orderItems.add( + new OrderManager.OrderItem( + ci.displayName, + ci.material.name(), + ci.unitPrice, + ci.unitPrice, + ci.quantity)); + } + String dType = "none"; + String cCode = ""; + if (usedCouponCode != null) { + dType = "coupon"; + cCode = usedCouponCode; + } else if (ecoPct > 0) { + dType = "eco"; + } + plugin.getOrderManager().recordOrder( + p, orderItems, originalTotal, + baseTotal + packFee, + originalTotal - baseTotal, + dType, cCode, packFee, + useShulker ? (packFee > 0 + ? "custom" : "default") + : "none", + color != null ? color.name() : ""); + // 获取最新订单 + synchronized (plugin.getOrderManager() + .getOrders()) { + List all = + plugin.getOrderManager() + .getOrders(); + if (!all.isEmpty()) + latestOrder = + all.get(all.size() - 1); + } + // 散装打印小票 + if (!useShulker && latestOrder != null) { + plugin.getOrderManager() + .sendReceiptChat(p, latestOrder); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + // + ex.getMessage()); + } + +// ===== 发货 ===== + if (!useShulker) { + for (CartItem ci : cart) { + ShopItem si = findItemById(ci.itemId); + ItemStack give = (si != null) + ? getShopStack(si, ci.quantity) + : new ItemStack(ci.material, ci.quantity); + HashMap left2 = + p.getInventory().addItem(give); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } else { + List shulkers = + packShulkers(cart, color); + + // ★ 给潜影盒打订单ID标签 + if (latestOrder != null) { + for (ItemStack sh : shulkers) { + ItemMeta shm = sh.getItemMeta(); + if (shm != null) { + List lore = shm.getLore() != null + ? new ArrayList<>(shm.getLore()) + : new ArrayList<>(); + lore.add("§8#" + latestOrder.orderId); + shm.setLore(lore); + sh.setItemMeta(shm); + } + } + } + + // 小票书塞入 + if (!shulkers.isEmpty() + && latestOrder != null) { + ItemStack book = plugin.getOrderManager() + .createReceiptBook(latestOrder); + plugin.getOrderManager() + .addBookToShulker(shulkers.get(0), book); + } + // 发货 + for (ItemStack sh : shulkers) { + HashMap left2 = + p.getInventory().addItem(sh); + for (ItemStack drop : left2.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + } + } + + + // ===== 退款记录 ===== + long now = System.currentTimeMillis(); + if (!useShulker) { + for (CartItem ci : cart) { + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + ci.itemId, ci.displayName, + ci.material, null, + ci.quantity, + ci.unitPrice * ci.quantity, + now)); + } + } else { + String summary = ""; + for (CartItem ci : cart) { + if (!summary.isEmpty()) summary += "+"; + summary += ci.displayName + + "x" + ci.quantity; + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + "cart_" + now, summary, + Material.SHULKER_BOX, color, + 1, baseTotal, now)); + } + + // ===== 核销优惠券 ===== + if (usedCouponCode != null) { + activeDiscount.remove(uuid); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCouponCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCouponCode + + " §7已核销"); + } + + shoppingCarts.remove(uuid); + pendingCheckout.remove(uuid); + return true; + } + + @EventHandler + public void onBlockPlace(org.bukkit.event.block.BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item == null) return; + if (!item.getType().name().contains("SHULKER")) return; + markShulkerUsed(item); + } + + // ★ 公共方法:标记潜影盒为已使用 + private void markShulkerUsed(ItemStack item) { + if (item == null) return; + ItemMeta meta = item.getItemMeta(); + if (meta == null || meta.getLore() == null) return; + for (String line : meta.getLore()) { + if (line.startsWith("§8#")) { + try { + long orderId = Long.parseLong( + line.replace("§8#", "").trim()); + // shulkerUsedOrders.add(orderId); + } catch (NumberFormatException ignored) {} + } + } + } + + @EventHandler + public void onPlayerDrop(org.bukkit.event.player.PlayerDropItemEvent e) { + ItemStack item = e.getItemDrop().getItemStack(); + if (item != null && item.getType().name().contains("SHULKER")) { + markShulkerUsed(item); + } + } + + + // ===== 潠影盒打包(指定颜色)===== + private List packShulkers( + List items, Material color) { + List result = new ArrayList<>(); + List all = new ArrayList<>(); + for (CartItem ci : items) { + int rem = ci.quantity; + while (rem > 0) { + int sz = Math.min(rem, 64); + all.add(new ItemStack(ci.material, sz)); + rem -= sz; + } + } + while (!all.isEmpty()) { + ItemStack shulker = new ItemStack(color); + org.bukkit.inventory.meta.BlockStateMeta meta = + (org.bukkit.inventory.meta.BlockStateMeta) + shulker.getItemMeta(); + if (meta != null) { + org.bukkit.block.BlockState state = + meta.getBlockState(); + if (state instanceof org.bukkit.block.Container) { + org.bukkit.block.Container c = + (org.bukkit.block.Container) state; + List remain = new ArrayList<>(); + for (ItemStack stack : all) { + HashMap left = + c.getInventory().addItem(stack); + for (ItemStack l : left.values()) + remain.add(l); + } + all = remain; + meta.setBlockState(c); + shulker.setItemMeta(meta); + } + } + result.add(shulker); + } + + return result; + } + + // ===== 打包确认GUI ===== + public void openPackingConfirm(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 27, "§6§l选择包装方式"); + g.setItem(10, mkItem(Material.CHEST, + "§a§l不打包", + "§7直接放入背包", + "§7适合少量物品")); + g.setItem(13, mkItem(Material.SHULKER_BOX, + "§6§l默认打包", + "§7随机颜色潜影盒", + "§7加收 §e5 §6枚债券")); + g.setItem(16, mkItem(Material.ENDER_CHEST, + "§b§l自选颜色", + "§7选择你喜欢的潜影盒颜色", + "§7加收 §e5 §6枚债券")); + g.setItem(22, mkItem(Material.PAPER, + "§7消费金额: §e" + baseTotal + "枚")); + + p.openInventory(g); + } + + public boolean handlePackingConfirm( + Player p, int raw) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + + int originalTotal = 0; + for (CartItem ci : cart) + originalTotal += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + + if (raw == 10) { + ecoPct = 2; + } + + double factor = 1.0; + if (ecoPct > 0) + factor *= (100 - ecoPct) / 100.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + originalTotal * factor); + + int totalQty = 0; + for (CartItem ci : cart) + totalQty += ci.quantity; + boolean multiAndBig = (cart.size() > 1 + && originalTotal >= 100); + + if (raw == 10) { + deductAndGive(p, cart, discounted, + false, null, "", 0, + ecoPct, couponPct); + return true; + } + if (raw == 13) { + Material c = Material.SHULKER_BOX; + if (multiAndBig) { + deductAndGive(p, cart, discounted, + true, c, "", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, + true, c, "默认打包", 5, + ecoPct, couponPct); + } + return true; + } + if (raw == 16) { + pendingCheckout.put(uuid, cart); + openColorSelect(p, discounted); + return true; + } + return true; + } + + + // ===== 自选颜色GUI ===== + public void openColorSelect(Player p, int baseTotal) { + Inventory g = Bukkit.createInventory( + null, 54, "§b§l选择潜影盒颜色"); + String[] colorNames = { + "白色","橙色","品红","淡蓝", + "黄色","淡绿","粉色","灰色", + "淡灰","青色","紫色","蓝色", + "棕色","绿色","红色","黑色","原色" + }; + for (int i = 0; i < SHULKER_COLORS.length; i++) { + ItemStack is = new ItemStack(SHULKER_COLORS[i]); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName("§e" + colorNames[i] + + "§6潜影盒"); + im.setLore(java.util.Arrays.asList( + "§7加收 §e5 §6枚债券", + "", + "§a点击选择")); + is.setItemMeta(im); + } + g.setItem(i, is); + } + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + g.setItem(45, mkItem(Material.PAPER, + "§7消费: §e" + baseTotal + + "枚 + §e5§6打包费")); + + p.openInventory(g); + } + + public boolean handleColorSelect( + Player p, int raw) { + if (raw == 49) { + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.get(uuid); + if (cart != null) { + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + openPackingConfirm(p, total); + } + return true; + } + if (raw < 0 || raw >= SHULKER_COLORS.length) + return false; + UUID uuid = p.getUniqueId(); + List cart = + pendingCheckout.remove(uuid); + if (cart == null) return false; + int total = 0; + for (CartItem ci : cart) + total += ci.unitPrice * ci.quantity; + + int couponPct = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int ecoPct = 0; + double factor = 1.0; + if (couponPct > 0) + factor *= (100 - couponPct) / 100.0; + int discounted = (int) Math.round( + total * factor); + + Material selectedColor = SHULKER_COLORS[raw]; + if (selectedColor == Material.SHULKER_BOX) { + deductAndGive(p, cart, discounted, true, + Material.SHULKER_BOX, + "原色打包", 0, + ecoPct, couponPct); + } else { + deductAndGive(p, cart, discounted, true, + selectedColor, "自选颜色", 5, + ecoPct, couponPct); + } + return true; + } + + + + // ===== 加载分类 ===== + + public void loadCategories() { + // plugin.getLogger().info("[Shop] === loadCategories 被调用 ==="); // 过滤调试日志 + categories.clear(); + viewCat.clear(); // ★ 清缓存 + viewPage.clear(); + + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + + convertTxtToMd(dir); + + File[] allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + if (allMd == null || allMd.length == 0) { + createDefaultCategories(dir); + allMd = dir.listFiles( + (d, n) -> n.endsWith(".md") + && !n.equals("coupons.md")); + } + if (allMd == null) { + // plugin.getLogger().warning( + // "[Shop] shop目录为空"); + return; + } + + /* plugin.getLogger().info("[Shop] === 开始加载 ==="); + plugin.getLogger().info("[Shop] 扫描到 " + + allMd.length + " 个md文件"); +*/ + for (File f : allMd) { + /* plugin.getLogger().info("[Shop] 读取: " + + f.getName());*/ + ShopCategory cat = parseFile(f); + if (cat != null) { + /* plugin.getLogger().info("[Shop] 分类: " + + cat.getName() + + " 商品数: " + + cat.getItems().size());*/ + if (!cat.getItems().isEmpty()) { + categories.add(cat); + } + } + } + + int totalItems = 0; + for (ShopCategory c : categories) + totalItems += c.getItems().size(); + + plugin.getLogger().info("[Shop] === 加载完成 ===" + + " 分类=" + categories.size() + + " 商品=" + totalItems); + } + + +// ===== txt → md 自动迁移 ===== + + private void convertTxtToMd(File dir) { + File[] txtFiles = dir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("coupons.txt")); + if (txtFiles == null) return; + for (File txt : txtFiles) { + String mdName = txt.getName() + .replace(".txt", ".md"); + File mdFile = new File(dir, mdName); + if (mdFile.exists()) continue; + + try { + List lines = new ArrayList<>(); + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(txt), + StandardCharsets.UTF_8)); + String line; + while ((line = br.readLine()) != null) + lines.add(line); + br.close(); + + String catName = txt.getName() + .replace(".txt", ""); + List out = new ArrayList<>(); + out.add("# " + catName); + out.add(""); + out.add("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + out.add("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + + for (String l : lines) { + l = l.trim(); + if (l.isEmpty() || l.startsWith("#")) + continue; + // 旧格式: ID | 名称 | 材质 | ... + // 保持原样转换为 md 表格行 + out.add("| " + l.replace("|", "|") + + " |"); + } + + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(mdFile), + StandardCharsets.UTF_8)); + for (String o : out) + pw.println(o); + pw.flush(); + pw.close(); + + txt.delete(); + // plugin.getLogger().info( + // "[Shop] 已迁移: " + // + txt.getName() + // + " → " + mdName); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 迁移失败: " + // + txt.getName()); + } + } + } + +// ===== 解析 md 文件 ===== + + private ShopCategory parseFile(File f) { + String name = f.getName() + .replace(".md", ""); + ShopCategory cat = + new ShopCategory(name, f.getName()); + try { + BufferedReader br = new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets.UTF_8)); + String line; + boolean headerPassed = false; + boolean separatorPassed = false; + + while ((line = br.readLine()) != null) { + line = line.trim(); + + // 空行跳过 + if (line.isEmpty()) continue; + + // 标题行:# 分类名 + if (line.startsWith("#")) { + String t = line.replaceFirst( + "^#+\\s*", "").trim(); + if (!t.isEmpty()) { + // 更新分类名(用文件内标题) + // cat.name 是 final,不改 + } + continue; + } + + // 表格分隔行:| --- | --- | + if (line.contains("---")) { + separatorPassed = true; + continue; + } + + // 表头行:| ID | 品名 | ... + if (line.startsWith("|")) { + if (!separatorPassed) { + headerPassed = true; + continue; + } + // 数据行 + ShopItem item = + parseTableRow(line); + if (item != null) + cat.addItem(item); + } + } + br.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 解析失败: " + f.getName() + // + " 原因: " + e.getMessage()); + return null; + } + return cat; + } + +// ===== 解析表格行 ===== + + private ShopItem parseTableRow(String row) { + // plugin.getLogger().info("[Shop] 行: " + row); // 过滤调试日志 + + String trimmed = row.trim(); + if (trimmed.startsWith("|")) + trimmed = trimmed.substring(1); + if (trimmed.endsWith("|")) + trimmed = trimmed.substring(0, + trimmed.length() - 1); + + String[] p = trimmed.split("\\|"); + // plugin.getLogger().info("[Shop] 列数=" + p.length); // 过滤调试日志 + + if (p.length < 6) { + // plugin.getLogger().warning( + // "[Shop] ✗ 列数不足, 跳过"); + return null; + } + + String rawId = p[0].trim(); + String rawName = p[1].trim(); + String rawMat = p[2].trim().toUpperCase(); + String rawBuy = p[3].trim(); + String rawSell = p[4].trim(); + String rawStock = p[5].trim(); + + // plugin.getLogger().info("[Shop] ID=" + // + rawId + " 名=" + rawName + // + " 材质=" + rawMat + // + " 购=" + rawBuy + " 售=" + rawSell + // + " 库=" + rawStock); // 过滤调试日志 + + // 检查材质是否存在 + Material mat = Material.matchMaterial(rawMat); + if (mat == null) { + // plugin.getLogger().warning( + // "[Shop] ✗ 无效材质: " + rawMat + // + " (行: " + row + ")"); + return null; + } + + try { + ShopItem item = new ShopItem( + rawId, rawName, mat, + Integer.parseInt(rawBuy), + Integer.parseInt(rawSell), + Integer.parseInt(rawStock), + p.length > 6 + ? Integer.parseInt( + p[6].trim()) : 0, + p.length > 7 + ? Integer.parseInt( + p[7].trim()) : 0); + // plugin.getLogger().info("[Shop] ✓ 解析成功: " + rawId); // 过滤调试日志 + return item; + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] ✗ 数字解析失败: " + // + e.getMessage()); + return null; + } + } + + +// ===== 保存为 md ===== + + public void saveCategory(ShopCategory cat) { + File dir = new File( + plugin.getDataFolder(), "shop"); + if (!dir.exists()) dir.mkdirs(); + File f = new File(dir, cat.getFileName()); + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.println("# " + cat.getName()); + pw.println(""); + pw.println("| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |"); + pw.println("| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |"); + for (ShopItem item : cat.getItems()) { + pw.println("| " + + item.getId() + + " | " + item.getDisplayName() + + " | " + item.getMaterial().name() + + " | " + item.getBuyPrice() + + " | " + item.getSellPrice() + + " | " + item.getStock() + + " | " + item.getHourlySales() + + " | " + item.getTotalSales() + + " |"); + } + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 保存失败: " + // + cat.getFileName()); + } + } + +// ===== 创建示例分类 ===== + + private void createDefaultCategories(File dir) { + writeMd(dir, "建筑材料.md", + "# 建筑材料\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| STONE | 石头 | STONE" + + " | 10 | -1 | -1 | 0 | 0 |\n" + + "| COBBLESTONE | 圆石 | COBBLESTONE" + + " | 5 | -1 | -1 | 0 | 0 |\n" + + "| OAK_PLANKS | 橡木木板 | OAK_PLANKS" + + " | 8 | 3 | -1 | 0 | 0 |\n" + + "| BRICKS | 砖块 | BRICKS" + + " | 12 | -1 | -1 | 0 | 0 |\n" + + "| GLASS | 玻璃 | GLASS" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| QUARTZ_BLOCK | 石英块 | QUARTZ_BLOCK" + + " | 20 | 8 | -1 | 0 | 0 |\n"); + + writeMd(dir, "装备工具.md", + "# 装备工具\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| IRON_PICKAXE | 铁镐 | IRON_PICKAXE" + + " | 50 | 20 | -1 | 0 | 0 |\n" + + "| DIAMOND_PICKAXE | 钻石镐 | DIAMOND_PICKAXE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| IRON_SWORD | 铁剑 | IRON_SWORD" + + " | 40 | 16 | -1 | 0 | 0 |\n" + + "| DIAMOND_SWORD | 钻石剑 | DIAMOND_SWORD" + + " | 400 | 160 | -1 | 0 | 0 |\n" + + "| ELYTRA | 鞘翅 | ELYTRA" + + " | 2000 | 800 | -1 | 0 | 0 |\n"); + + writeMd(dir, "食物补给.md", + "# 食物补给\n" + + "\n" + + "| ID | 品名 | 材质" + + " | 购入价 | 售出价" + + " | 库存" + + " | 本小时销量 | 总销量 |\n" + + "| --- | --- | ---" + + " | --- | --- | ---" + + " | --- | --- |\n" + + "| COOKED_BEEF | 熟牛排 | COOKED_BEEF" + + " | 15 | 6 | -1 | 0 | 0 |\n" + + "| GOLDEN_APPLE | 金苹果 | GOLDEN_APPLE" + + " | 500 | 200 | -1 | 0 | 0 |\n" + + "| BREAD | 面包 | BREAD" + + " | 8 | 3 | -1 | 0 | 0 |\n"); + + // plugin.getLogger().info( + // "[Shop] 已创建3个示例分类(md)"); + } + + private void writeMd(File dir, String name, + String content) { + File f = new File(dir, name); + if (f.exists()) return; + try { + PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets.UTF_8)); + pw.print(content); + pw.flush(); + pw.close(); + } catch (Exception e) { + // plugin.getLogger().warning( + // "[Shop] 创建失败: " + name); + } + } + + /** + * 从商品名称+材质智能推断完整物品 + * + * "夜视药水" + POTION + * → PotionEffect(NIGHT_VISION, 600, 0) + * + * "喷溅型治疗药水" + SPLASH_POTION + * → PotionEffect(HEALING, 600, 0) + * + * "长效夜视之箭" + TIPPED_ARROW + * → PotionEffect(NIGHT_VISION, 1800, 0) + * + * "强效力量喷溅药水" + SPLASH_POTION + * → PotionEffect(STRENGTH, 600, 1) + */ + private ItemStack inferFromName( + String name, Material mat) { + // 只处理药水和箭 + if (mat != Material.POTION + && mat != Material.SPLASH_POTION + && mat != Material.LINGERING_POTION + && mat != Material.TIPPED_ARROW) { + return null; + } + + // 1. 找效果类型 + PotionEffectType effectType = null; + for (Map.Entry e + : EFFECT_MAP.entrySet()) { + if (name.contains(e.getKey())) { + effectType = e.getValue(); + break; + } + } + if (effectType == null) return null; + + // 2. 找时长 + int duration = 600; + if (name.contains("长效") || name.contains("长时") + || name.contains("持久") || name.contains("长命")) { + duration = 1800; + } +// ★ 瞬间效果药水:duration = 1 + if (name.contains("治疗") || name.contains("治愈") + || name.contains("伤害")) { + duration = 1; + } + + // 3. 找等级 + int amplifier = 0; + if (name.contains("强效") + || name.contains("强化") + || name.contains("加强")) { + amplifier = 1; + } + + // 4. 构造完整物品 + try { + ItemStack item = new ItemStack(mat); + PotionMeta meta = + (PotionMeta) item.getItemMeta(); + + // ★ 神龟药水:双效果 + if (name.contains("神龟")) { + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.RESISTANCE, + duration, amplifier + 2), + true); + meta.addCustomEffect( + new PotionEffect( + PotionEffectType.SLOWNESS, + duration, amplifier + 3), + true); + } else { + // 普通药水:单效果 + meta.addCustomEffect( + new PotionEffect(effectType, + duration, amplifier), + true); + } + + item.setItemMeta(meta); + return item; + } catch (Exception e) { + return null; + } + } + + public void saveAll() { + for (ShopCategory cat : categories) + saveCategory(cat); + } + private ItemStack getShopStack( + ShopItem item, int amount) { + // 优先级1: 手动捕获的NBT数据 + ItemStack cached = + itemStackCache.get(item.getId()); + if (cached != null) { + ItemStack copy = cached.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级2: 根据名称智能推断 + ItemStack inferred = inferFromName( + item.getDisplayName(), + item.getMaterial()); + if (inferred != null) { + ItemStack copy = inferred.clone(); + copy.setAmount(amount); + return copy; + } + // 优先级3: 纯材质(默认) + return new ItemStack( + item.getMaterial(), amount); + } + + // ===== 购买 ===== + + public boolean buyItem(Player p, ShopItem item, + int amount) { + // plugin.getLogger().info("[Shop] ★ buyItem item=" + (item != null ? item.getId() : "null") + " amount=" + amount); + // plugin.getLogger().info("buyItem被调用"); + if (item == null) return false; + if (item.getStock() >= 0 + && item.getStock() < amount) { + p.sendMessage("§c库存不足,剩余: " + + item.getStock()); + return false; + } + if (p.getInventory().firstEmpty() == -1 + && !p.getInventory().contains( + item.getMaterial(), 1)) { + p.sendMessage("§c背包已满"); + return false; + } + + int unit = item.getBuyPrice(); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + int total = Math.max(0, + unit * amount * (100 - disc) / 100); + + int balance = plugin.getBonds() + .getBonds(p.getName()); + if (balance < total) { + p.sendMessage("§c债券不足,需要 §e" + + total + "§c枚,当前 §e" + + balance + "§c枚"); + return false; + } + + boolean ok = plugin.getBonds().deductBonds( + p.getName(), total, + "shop_buy", item.getId(), + "商店系统", "购买" + + item.getDisplayName()); + if (!ok) { + p.sendMessage("§c扣款失败"); + return false; + } + + ItemStack give = getShopStack(item, amount); + HashMap left = + p.getInventory().addItem(give); + for (ItemStack drop : left.values()) + p.getWorld().dropItemNaturally( + p.getLocation(), drop); + + if (item.getStock() >= 0) + item.setStock( + item.getStock() - amount); + item.addSales(amount); + + mergeOrAddRefund(p, item, amount, total); + Long applyTime = couponApplyTime.get(p.getUniqueId()); + if (applyTime != null + && System.currentTimeMillis() - applyTime > 300000L) { + // 超过5分钟,自动失效 + pendingCouponCode.remove(p.getUniqueId()); + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + p.sendMessage("§7优惠券已过期,请重新输入"); + disc = 0; // 重置折扣 + } + + + String discMsg = disc > 0 + ? " §7(优惠" + disc + "%)" : ""; + p.sendMessage("§a购买成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a-" + total + "枚债券" + + discMsg); + if (disc > 0) + activeDiscount.remove( + p.getUniqueId()); + // ★ 直购成功后核销优惠券 + String usedCode = pendingCouponCode + .remove(p.getUniqueId()); + if (usedCode != null) { + activeDiscount.remove(p.getUniqueId()); + couponApplyTime.remove(p.getUniqueId()); + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + m.invoke(sdf1, usedCode); + } + } catch (Exception ignored) {} + p.sendMessage("§7优惠券 §e" + usedCode + + " §7已核销"); + couponApplyTime.remove(p.getUniqueId()); + } + // ★ 记录订单(包裹try-catch,失败不影响购买) + try { + String usedCouponCode = + pendingCouponCode.get(p.getUniqueId()); + List orderItems = + new ArrayList<>(); + orderItems.add(new OrderManager.OrderItem( + item.getDisplayName(), + item.getMaterial().name(), + item.getBuyPrice(), + item.getBuyPrice(), amount)); + plugin.getOrderManager().recordOrder( + p, orderItems, + item.getBuyPrice() * amount, total, + item.getBuyPrice() * amount - total, + usedCouponCode != null ? "coupon" : "none", + usedCouponCode != null ? usedCouponCode : "", + 0, "none", ""); + // 打印小票 + List all = + plugin.getOrderManager().getOrders(); + if (!all.isEmpty()) { + plugin.getOrderManager().sendReceiptChat( + p, all.get(all.size() - 1)); + } + } catch (Exception ex) { + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + ex.getMessage()); + } + + return true; + } + + + public void handleRefundItemClick(Player p, int raw) { + if (raw < 10 || raw >= 45) return; + int target = raw - 10; + int cnt = 0; + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { it.remove(); continue; } + if (cnt == target) { + requestRefund(p, r); + return; + } + cnt++; + } + } + } + + + // ===== 出售 ===== + + public boolean sellItem(Player p, ShopItem item, + int amount) { + if (item == null) return false; + + // ★ 先判断售价,禁止出售的不收物品 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return false; + } + + if (!p.getInventory().containsAtLeast( + getShopStack(item, 1), amount)) { + p.sendMessage("§c你没有足够的 §e" + + item.getDisplayName()); + return false; + } + + int total = item.getEffectiveSellPrice() * amount; + + p.getInventory().removeItem( + getShopStack(item, amount)); + plugin.getBonds().addBonds( + p.getName(), total, "shop_sell", + item.getId(), "商店系统", + "出售" + item.getDisplayName()); + if (item.getStock() >= 0) + item.setStock(item.getStock() + amount); + p.sendMessage("§a出售成功: §e" + + item.getDisplayName() + + " x" + amount + + " §a+" + total + "枚债券"); + return true; + } + + // ===== 退款合并:30秒内同商品合并为一个订单 ===== + private boolean mergeOrAddRefund(Player p, ShopItem item, + int amount, int total) { + long now = System.currentTimeMillis(); + synchronized (refundRecords) { + for (int i = refundRecords.size() - 1; i >= 0; i--) { + RefundRecord r = refundRecords.get(i); + boolean same = r.playerUUID.equals(p.getUniqueId()) + && r.itemId.equals(item.getId()) + && (now - r.timestamp) < 30000L; + if (same) { + // final 字段不能改,只能删旧建新 + int newAmount = r.amount + amount; + int newTotal = r.totalPaid + total; + refundRecords.remove(i); + refundRecords.add(new RefundRecord( + r.playerUUID, r.playerName, + r.itemId, r.itemName, + r.material, newAmount, + newTotal, now)); + return true; + } + } + } + refundRecords.add(new RefundRecord( + p.getUniqueId(), p.getName(), + item.getId(), item.getDisplayName(), + item.getMaterial(), amount, total, now)); + return false; + } + + + + // ===== 退款 ===== + + public boolean requestRefund(Player p, RefundRecord rec) { + if (rec == null) return false; + if (!rec.playerUUID.equals(p.getUniqueId())) { + p.sendMessage("§c这不是你的购买记录"); + return false; + } + if (rec.isExpired()) { + p.sendMessage("§c已超过5分钟退款期限"); + synchronized (refundRecords) { + refundRecords.remove(rec); + } + return false; + } + + // 检测物品/潜影盒是否还在 + synchronized (refundRecords) { + if (rec.shulkerMaterial != null) { + int boxCount = countShulkers(p, rec.shulkerMaterial); + if (boxCount < 1) { + p.sendMessage("§c潜影盒已移出背包,无法退款"); + refundRecords.remove(rec); + return false; + } + } else { + int have = countDirect(p, rec.material); + if (have < rec.amount) { + p.sendMessage("§c物品已转移或消耗,无法退款"); + refundRecords.remove(rec); + return false; + } + } + } + + // 退款:扣物品/潜影盒 + if (rec.shulkerMaterial != null) { + // 盒装:扣一个潜影盒 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == rec.shulkerMaterial) { + p.getInventory().clear(i); + break; + } + } + } else { + // 普通:扣商品 + p.getInventory().removeItem( + new ItemStack(rec.material, rec.amount)); + } + + // 退款:加债券 + plugin.getBonds().addBonds( + p.getName(), rec.totalPaid, + "refund", rec.itemId, + "商店系统", "退款" + rec.itemName); + + // 恢复库存 + ShopItem si = findItemById(rec.itemId); + if (si != null && si.getStock() >= 0) + si.setStock(si.getStock() + rec.amount); + + synchronized (refundRecords) { + refundRecords.remove(rec); + } + + p.sendMessage("§a退款成功: §e" + rec.itemName + + " §a+" + rec.totalPaid + "枚债券"); + return true; + } + + + + + public void setPlayerDiscount(Player p, + int pct) { + activeDiscount.put( + p.getUniqueId(), pct); + p.sendMessage("§a已设置 " + pct + + "% 折扣,下次购买生效"); + } + + // ===== 管理员操作 ===== + + public boolean addItemToCategory( + String catName, String id, + String displayName, Material mat, + int buyPrice, int sellPrice, + int stock) { + ShopCategory cat = + findCategory(catName); + if (cat == null) return false; + if (cat.getItem(id) != null) return false; + cat.addItem(new ShopItem(id, displayName, + mat, buyPrice, sellPrice, + stock, 0, 0)); + saveCategory(cat); + return true; + } + + public boolean removeItem(String id) { + for (ShopCategory cat : categories) { + ShopItem item = cat.getItem(id); + if (item != null) { + cat.getItems().remove(item); + saveCategory(cat); + return true; + } + } + return false; + } + + public boolean setStock(String id, int stock) { + ShopItem item = findItemById(id); + if (item == null) return false; + item.setStock(stock); + for (ShopCategory cat : categories) { + if (cat.getItem(id) != null) { + saveCategory(cat); + return true; + } + } + return false; + } + + public ShopCategory findCategory(String n) { + for (ShopCategory c : categories) + if (c.getName().equals(n)) return c; + return null; + } + + public ShopItem findItemById(String id) { + for (ShopCategory c : categories) { + ShopItem item = c.getItem(id); + if (item != null) return item; + } + return null; + } + + public List getCategories() { + return categories; + } + + // ===== 命令处理 ===== + + public boolean handleCommand( + CommandSender s, String[] a) { + if (a.length <= 1) { + if (s instanceof Player) { + openShopMain((Player) s); + } else { + s.sendMessage("§c仅玩家可使用"); + } + return true; + } + + String sub = a[1].toLowerCase(); + + if (sub.equals("reload")) { + loadCategories(); + s.sendMessage("§a商店已重载,共 " + + categories.size() + + " 个分类"); + return true; + } + + if (sub.equals("add")) { + if (a.length < 7) { + s.sendMessage( + "§e用法: shop add <分类>" + + " <名称> <材质>" + + " <购入价>" + + " [售出价-1自动]" + + " [库存-1无限]"); + return true; + } + Material mat; + try { + mat = Material.valueOf( + a[5].toUpperCase()); + } catch (Exception e) { + s.sendMessage( + "§c无效材质: " + a[5]); + return true; + } + int price = Integer.parseInt(a[6]); + int sell = a.length > 7 + ? Integer.parseInt(a[7]) : -1; + int stock = a.length > 8 + ? Integer.parseInt(a[8]) : -1; + if (addItemToCategory(a[2], a[3], + a[4], mat, price, sell, stock)) { + s.sendMessage("§a已添加: " + + a[4] + " → " + a[2]); + } else { + s.sendMessage("§c添加失败" + + "(分类不存在或ID重复)"); + } + return true; + } + + if (sub.equals("remove")) { + if (a.length < 3) { + s.sendMessage( + "§e用法: shop remove "); + return true; + } + if (removeItem(a[2])) { + s.sendMessage( + "§a已移除: " + a[2]); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("set")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop set " + + " <库存数(-1无限)>"); + return true; + } + int stock = Integer.parseInt(a[3]); + if (setStock(a[2], stock)) { + s.sendMessage("§a已设置 " + + a[2] + " 库存为 " + + stock); + } else { + s.sendMessage( + "§c未找到: " + a[2]); + } + return true; + } + + if (sub.equals("pro")) { + if (a.length < 4) { + s.sendMessage( + "§e用法: shop pro" + + " <玩家名> <折扣%>"); + return true; + } + Player t = + Bukkit.getPlayerExact(a[2]); + if (t == null) { + s.sendMessage("§c玩家不在线"); + return true; + } + int pct = Integer.parseInt(a[3]); + setPlayerDiscount(t, pct); + s.sendMessage("§a已对 " + a[2] + + " 设置 " + pct + "% 折扣"); + return true; + } + + + s.sendMessage("§e商店子命令:" + + " reload / add / remove" + + " / set / pro "); + return true; + } + @EventHandler(priority = EventPriority.LOW) + public void onCouponChat( + org.bukkit.event.player.AsyncPlayerChatEvent e) { + Player p = e.getPlayer(); + UUID uuid = p.getUniqueId(); + if (!couponListening.contains(uuid)) return; + e.setCancelled(true); + couponListening.remove(uuid); + String code = e.getMessage().trim(); + if (code.isEmpty()) { + p.sendMessage("§c已取消输入"); + return; + } + // ★ 预处理:标准化输入 + String normalized = normalizeCouponInput(code); + if (normalized.isEmpty()) { + p.sendMessage("§c优惠券码无效(输入为空或仅含特殊字符)"); + couponListening.remove(uuid); + return; + } + + p.sendMessage("§7正在验证优惠券: §e" + code); + + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "validateCoupon", + String.class); + Object result = m.invoke(sdf1, normalized); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception ex) { + p.sendMessage("§c验证异常: " + + ex.getMessage()); + return; + } + if (discount <= 0) { + p.sendMessage("§c优惠券无效或已使用"); + return; + } + int offPct = 100 - discount; + int current = activeDiscount + .getOrDefault(uuid, 0); + if (offPct > current) { + pendingCouponCode.put(uuid, normalized); + activeDiscount.put(uuid, offPct); + couponApplyTime.put(uuid, System.currentTimeMillis()); + String zhe; + if (discount % 10 == 0) + zhe = (discount / 10) + "折"; + else + zhe = (discount / 10.0) + "折"; + p.sendMessage("§6§l[优惠券] §a验证通过!"); + p.sendMessage("§7折扣: §e" + zhe + + " §7(减" + offPct + "%)"); + p.sendMessage("§7结算时自动核销"); + final UUID u = uuid; + Bukkit.getScheduler().runTask(plugin, () -> { + Player pl = Bukkit.getPlayer(u); + if (pl != null && pl.isOnline()) { + openShopMain(pl); + } + }); + } else { + String curZhe; + if ((100 - current) % 10 == 0) + curZhe = ((100 - current) / 10) + "折"; + else + curZhe = ((100 - current) / 10.0) + "折"; + p.sendMessage("§c当前已有更优折扣: " + + curZhe + ",本次不生效"); + } + } + + /** + * 优惠券输入标准化 + * 全角→半角、去分隔符、转小写 + * "AAA-BBB-CCCC" → "aaaabbbbcccc" + * "AAA-BBB" → "aaabbb" + * "aaa_bbb_cccc" → "aaabbbcccc" + */ + private String normalizeCouponInput(String s) { + if (s == null) return ""; + StringBuilder sb = new StringBuilder(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + // 全角数字→半角 + if (c >= '\uFF10' && c <= '\uFF19') { + sb.append((char)(c - 0xFF10 + '0')); + } + // 全角字母→半角 + else if (c >= '\uFF21' && c <= '\uFF3A') { + sb.append((char)(c - 0xFF21 + 'A')); + } + else if (c >= '\uFF41' && c <= '\uFF5A') { + sb.append((char)(c - 0xFF41 + 'a')); + } + // ★ 保留中文字符(Unicode CJK统一汉字范围) + else if ((c >= '\u4E00' && c <= '\u9FFF') + || (c >= '\u3400' && c <= '\u4DBF')) { + sb.append(c); + } + // 保留半角字母和数字 + else if ((c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9')) { + sb.append(c); + } + // 分隔符(- _ 空格等)丢弃 + } + return sb.toString().toLowerCase(); + } + + + + + + private String formatZhe(int discount) { + if (discount % 10 == 0) { + return (discount / 10) + "折"; + } + return (discount / 10.0) + "折"; + } + + public boolean redeemCoupon(Player p, String code) { + int discount = -1; + try { + Plugin sdf1 = Bukkit.getPluginManager() + .getPlugin("SDF1"); + if (sdf1 != null && sdf1.isEnabled()) { + java.lang.reflect.Method m = + sdf1.getClass().getMethod( + "redeemCoupon", + String.class); + Object result = m.invoke(sdf1, code); + discount = (result != null) + ? (Integer) result : -1; + } + } catch (Exception e) { + return false; + } + if (discount <= 0) return false; + int offPct = 100 - discount; + int cur = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (offPct > cur) { + activeDiscount.put(p.getUniqueId(), offPct); + } + return true; + } + + /** + * 商店主界面点击"优惠券"按钮后调用 + * 开启聊天监听,等待玩家输入优惠券码 + */ + public void openCouponInput(Player p) { + UUID uuid = p.getUniqueId(); + // 先取消已有的待核销优惠券 + pendingCouponCode.remove(uuid); + couponListening.add(uuid); + // 关闭UI + Bukkit.getScheduler().runTask(plugin, () -> { + if (p.isOnline()) p.closeInventory(); + }); + p.sendMessage("§6§l[优惠券] §e请在聊天栏输入优惠券码"); + p.sendMessage("§715秒内输入,过期自动取消"); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (couponListening.remove(uuid)) { + Player pl = Bukkit.getPlayer(uuid); + if (pl != null && pl.isOnline()) { + pl.sendMessage("§c优惠券输入已超时取消"); + } + } + }, 300L); + } + + + // ===== GUI: 主界面 ===== + + public void openShopMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l商店"); + int si = 0; + for (ShopCategory cat : categories) { + if (si >= ITEM_SLOTS.length) break; + ItemStack is = new ItemStack( + Material.CHEST); + ItemMeta im = is.getItemMeta(); + if (im != null) { + im.setDisplayName( + "§e§l" + cat.getName()); + im.setLore(Arrays.asList( + "§7商品: §f" + + cat.getItems().size() + + " 种", + "", + "§a点击进入")); + is.setItemMeta(im); + } + g.setItem(ITEM_SLOTS[si], is); + si++; + } + // ★ 改为跳转订单中心 + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看订单记录、申请退款", + "", "§a点击进入")); + + // 在 openShopMain 方法中,原来的 slot 49/53 附近加: + + g.setItem(48, mkItem( + isCartMode(p) + ? Material.LIME_WOOL + : Material.GRAY_WOOL, + isCartMode(p) + ? "§a§l购物车模式 §7[开]" + : "§7§l购物车模式 §7[关]", + isCartMode(p) + ? "§a当前: 点击商品加入购物车" + : "§7当前: 直接购买", + "§7点击切换")); + g.setItem(50, mkItem(Material.CHEST, + "§6§l购物车 §7[" + getCartCount(p) + "件]", + "§7总价: §6" + getCartTotal(p) + "枚", + "", + "§a点击打开购物车")); + + // 在现有按钮之后,p.openInventory(g) 之前加: + g.setItem(49, mkItem(Material.CLOCK, + "§6§l订单中心", + "§7查看购物订单记录", + "", "§a点击进入")); + + // 优惠券按钮 - slot 47 + String discStatus = "§7"; + int curDisc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (curDisc > 0) { + discStatus = "§a当前: " + formatZhe(100 - curDisc); + } + g.setItem(47, mkItem(Material.NAME_TAG, + "§6§l优惠券", + discStatus, + "", + "§a点击输入优惠券码", + "§7一次性使用,核销后失效")); + + g.setItem(53, mkItem(Material.ARROW, + "§7返回")); + + p.openInventory(g); + } + + // ===== GUI: 分类 ===== + public void openCategory(Player p, + ShopCategory cat, + int page) { + // ★ 每次重新获取最新对象 ★ + ShopCategory fresh = null; + for (ShopCategory c : categories) { + if (c.getName().equals(cat.getName())) { + fresh = c; + break; + } + } + if (fresh == null) { + openShopMain(p); + return; + } + cat = fresh; + viewCat.put(p.getUniqueId(), cat); + + int total = (int) Math.ceil( + (double) cat.getItems().size() + / PAGE_SIZE); + if (total < 1) total = 1; + if (page < 0) page = 0; + if (page >= total) page = total - 1; + viewPage.put(p.getUniqueId(), page); + + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l" + cat.getName()); + + + int start = page * PAGE_SIZE; + int end = Math.min(start + PAGE_SIZE, + cat.getItems().size()); + + for (int i = start; i < end; i++) { + int idx = i - start; + if (idx >= ITEM_SLOTS.length) break; + + // ★ item 保留原名,代表 ShopItem 数据 + ShopItem item = cat.getItems().get(i); + + // ★ is 是带完整元数据的 ItemStack(用于GUI图标) + ItemStack is = getShopStack(item, 1); + ItemMeta im = is.getItemMeta(); + + if (im != null) { + im.setDisplayName("§e§l" + + item.getDisplayName()); + List lore = new ArrayList<>(); + lore.add("§7购入: §a" + + item.getBuyPrice() + + "§7枚债券"); + lore.add("§7售出: §e" + + item.getEffectiveSellPrice() + + "§7枚债券"); + if (item.getStock() >= 0) + lore.add("§7库存: §f" + + item.getStock()); + else + lore.add("§7库存: §a无限"); + lore.add(""); + int disc = activeDiscount + .getOrDefault(p.getUniqueId(), 0); + if (disc > 0) { + int dis = item.getBuyPrice() + * (100 - disc) / 100; + lore.add("§7优惠价: §6" + + dis + "枚" + + " §7(-" + disc + "%)"); + } + lore.add("§a左键买1 §6右键卖1"); + lore.add("§aShift+左键买64" + + " §6Shift+右键卖64"); + im.setLore(lore); + is.setItemMeta(im); + } + + // ★ 用 is(ItemStack)放入GUI格子 + g.setItem(ITEM_SLOTS[idx], is); + } + + + g.setItem(4, mkItem(Material.BOOK, + "§e§l" + cat.getName(), + "§7共 " + cat.getItems().size() + + " 种商品")); + g.setItem(48, page > 0 + ? mkItem(Material.ARROW, + "§7上一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(49, mkItem(Material.PAPER, + "§e" + (page + 1) + "/" + total)); + g.setItem(50, page < total - 1 + ? mkItem(Material.ARROW, + "§7下一页") + : mkItem(Material + .BLACK_STAINED_GLASS_PANE, " ")); + g.setItem(53, mkItem(Material.ARROW, + "§7返回商店")); + + p.openInventory(g); + } + + // ===== GUI: 退款 ===== + + public void openRefundPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§c§l退款中心"); + + // 位移检测:比对上次记录的背包内容 + validateRefunds(p); + + // 过期清理 + synchronized (refundRecords) { + refundRecords.removeIf(RefundRecord::isExpired); + } + + int idx = 0; + synchronized (refundRecords) { + for (RefundRecord r : refundRecords) { + if (idx >= 45) break; + if (!r.playerUUID.equals(p.getUniqueId())) continue; + if (r.isExpired()) continue; + + // 盒装用潜影盒图标,普通用商品图标 + Material iconMat = (r.shulkerMaterial != null) + ? r.shulkerMaterial : r.material; + + ItemStack is = new ItemStack(iconMat); + ItemMeta im = is.getItemMeta(); + if (im != null) { + int secs = r.getSecondsLeft(); + im.setDisplayName("§e" + r.itemName + + (r.shulkerMaterial != null + ? " §7[盒装]" : " x" + r.amount)); + + List lore = new ArrayList<>(); + lore.add("§7订单: §f#" + r.timestamp); + lore.add("§7支付: §a" + r.totalPaid + + "§7枚债券"); + + if (r.shulkerMaterial != null) { + int boxCount = countShulkers(p, + r.shulkerMaterial); + lore.add("§7潜影盒: §f" + + (boxCount >= 1 + ? "§a在背包中" + : "§c已移出")); + } else { + int have = countDirect(p, r.material); + lore.add("§7物品: §f" + have + + "/" + r.amount); + } + + lore.add("§7剩余: §e" + secs + "秒"); + lore.add(""); + lore.add("§c点击退款"); + im.setLore(lore); + } + is.setItemMeta(im); + g.setItem(10 + idx, is); + idx++; + } + } + + if (idx == 0) { + g.setItem(22, mkItem(Material.BARRIER, + "§7暂无可退款记录")); + } + + g.setItem(49, mkItem(Material.ARROW, "§7返回")); + + p.openInventory(g); + } + // 统计背包中某物品数量 + private int countInInventory(Player p, Material mat) { + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == mat) { + count += is.getAmount(); + } + } + // 潜影盒也要检查里面 + for (ItemStack is : p.getInventory().getContents()) { + if (is != null + && is.getType().name() + .endsWith("_SHULKER_BOX")) { + count += is.getAmount(); + } + } + return count; + } + + // 只统计背包中直接可见的数量(不含盒内) + // 新代码:增加按完整物品匹配的方法 + private int countDirect(Player p, Material mat) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.getType() == mat) + count += is.getAmount(); + } + return count; + } + + private int countDirectByStack(Player p, + ItemStack target) { + int count = 0; + for (ItemStack is : + p.getInventory().getContents()) { + if (is != null && is.isSimilar(target)) + count += is.getAmount(); + } + return count; + } + // 统计背包中潜影盒数量 + private int countShulkers(Player p, Material shulker) { + if (shulker == null) return Integer.MAX_VALUE; + int count = 0; + for (ItemStack is : p.getInventory().getContents()) { + if (is != null && is.getType() == shulker) + count += is.getAmount(); + } + return count; + } + + // 退款检测:位移 OR 消耗 → 不能退 + private void validateRefunds(Player p) { + synchronized (refundRecords) { + Iterator it = + refundRecords.iterator(); + while (it.hasNext()) { + RefundRecord r = it.next(); + if (!r.playerUUID.equals(p.getUniqueId())) + continue; + if (r.isExpired()) { + it.remove(); + continue; + } + + if (r.shulkerMaterial != null) { + // 盒装:检测潜影盒是否还在背包 + int now = countShulkers( + p, r.shulkerMaterial); + if (now < 1) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 潜影盒已移出背包,无法退款"); + } + } else { + // 普通:检测商品是否还在背包 + int now = countDirect(p, r.material); + if (now < r.amount) { + it.remove(); + p.sendMessage("§c[退款] " + r.itemName + + " 物品已被使用或移出,无法退款"); + } + } + } + } + } + + + public int getPlayerPage(java.util.UUID uuid) { + return viewPage.getOrDefault(uuid, 0); + } + + public ShopCategory getPlayerCategory(java.util.UUID uuid) { + return viewCat.get(uuid); + } + + // ===== 供GUIManager调用:商店主界面点击 ===== + public boolean handleMainShopClick(Player p, int raw) { + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true; } + + if (raw == 48) { + toggleCartMode(p); + openShopMain(p); + return true; + } + if (raw == 50) { + openCart(p); + return true; + } + if (raw == 53) { + plugin.getGui().openMain(p); + return true; + } + + // ★ 新增:优惠券按钮 + if (raw == 47) { + openCouponInput(p); + return true; + } + if (raw == 49) { plugin.getOrderManager().openOrderCenter(p); return true;} + int idx = slotToIndex(raw); + if (idx < 0 || idx >= categories.size()) + return true; + openCategory(p, categories.get(idx), 0); + return true; + } + + // ===== 供GUIManager调用:退款中心点击 ===== + public boolean handleRefundClick(Player p, int raw) { + if (raw == 49) { openShopMain(p); return true; } + return true; + } + + // ===== 供GUIManager调用:分类页点击 ===== + public boolean handleCategoryClick(Player p, String title, + int raw, boolean left, + boolean shift) { + // plugin.getLogger().info("[Shop] ★ handleCategoryClick raw=" + raw); + + ShopManager sm = this; + ShopCategory match = null; + for (ShopCategory c : categories) { + if (title.equals("§6§l" + c.getName())) { + match = c; + break; + } + } + if (match == null) { + // plugin.getLogger().info("[Shop] ★ 匹配失败, title=[" + title + "]"); + return false; + } + // plugin.getLogger().info("[Shop] ★ 匹配到: " + match.getName()); + + viewCat.put(p.getUniqueId(), match); + + if (raw == 53) { openShopMain(p); return true; } + int pg = getPlayerPage(p.getUniqueId()); + if (raw == 48) { openCategory(p, match, pg - 1); return true; } + if (raw == 50) { openCategory(p, match, pg + 1); return true; } + + int itemIdx = slotToIndex(raw); + if (itemIdx < 0) { + // plugin.getLogger().info("[Shop] ★ slot=" + raw + " 不是商品格"); + return true; + } + int actual = itemIdx + pg * PAGE_SIZE; + if (actual < 0 || actual >= match.getItems().size()) { + // plugin.getLogger().info("[Shop] ★ 超出范围 actual=" + actual + " size=" + match.getItems().size()); + return true; + } + ShopItem si = match.getItems().get(actual); + // plugin.getLogger().info("[Shop] ★ 准备购买: " + si.getId() + " left=" + left); + + int amount = shift ? 64 : 1; + if (left) { + if (shift && isCartMode(p)) { addToCart(p, si); } + else if (shift) { buyItem(p, si, 64); } + else if (isCartMode(p)) { addToCart(p, si); } + else { buyItem(p, si, 1); } + } else { + if (shift) { sellAllOf(p, si); } + else { sellItem(p, si, 1); } + } + openCategory(p, match, pg); + return true; + } + + // ===== 批量出售某商品全部数量 ===== + private void sellAllOf(Player p, ShopItem item) { + if (item == null) return; + // ★ 补上禁止出售检查 + if (item.getEffectiveSellPrice() <= 0) { + p.sendMessage("§c该商品不可出售"); + return; + } + if (item == null) return; + Material mat = item.getMaterial(); + int total = 0; + int count = 0; + // 遍历背包清点 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + total += is.getAmount(); + count++; + } + } + if (total <= 0) { + p.sendMessage("§c你没有可出售的 §e" + + item.getDisplayName()); + return; + } + // 清除背包中该物品 + for (int i = 0; i < p.getInventory().getSize(); i++) { + ItemStack is = p.getInventory().getItem(i); + if (is != null && is.getType() == mat) { + p.getInventory().clear(i); + } + } + int money = item.getEffectiveSellPrice() * total; + plugin.getBonds().addBonds( + p.getName(), money, "shop_sell", + item.getId(), "商店系统", + "批量出售" + item.getDisplayName() + + " x" + total); + if (item.getStock() >= 0) + item.setStock(item.getStock() + total); + p.sendMessage("§a批量出售: §e" + + item.getDisplayName() + + " x" + total + + " §a+" + money + "枚债券"); + } + + + // ===== 定时 ===== + + private void startHourlyTask() { + new BukkitRunnable() { + @Override + public void run() { + // ★ 汇报交给 SalesStatsManager + if (plugin.getSalesStats() != null) { + plugin.getSalesStats().report(); + } + // 重置小时销量 + for (ShopCategory cat : categories) + for (ShopItem item : cat.getItems()) + item.setHourlySales(0); + saveAll(); + } + }.runTaskTimer(plugin, 72000L, 72000L); + } + + + private void startCleanupTask() { + new BukkitRunnable() { + @Override + public void run() { + synchronized (refundRecords) { + refundRecords.removeIf( + RefundRecord::isExpired); + } + } + }.runTaskTimer(plugin, 1200L, 1200L); + } + + // ===== 工具 ===== + + private int slotToIndex(int slot) { + for (int i = 0; i < ITEM_SLOTS.length; i++) + if (ITEM_SLOTS[i] == slot) return i; + return -1; + } + + private ItemStack mkItem(Material mat, + String name, + String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) + im.setLore(Arrays.asList(lore)); + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/TicketManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/TicketManager.java new file mode 100644 index 0000000..c21edc2 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/TicketManager.java @@ -0,0 +1,1644 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class TicketManager { + + private final Main plugin; + + public static final String T_TICKET_MAIN = + "§6§l工单中心"; + public static final String T_TICKET_CREATE = + "§e§l创建工单"; + public static final String T_TICKET_MY = + "§b§l我的工单"; + public static final String T_TICKET_ADMIN = + "§c§l工单管理"; + public static final String T_TICKET_DETAIL = + "§a§l工单详情"; + + public static final String TYPE_BUG = + "bug反馈"; + public static final String TYPE_HELP = + "求助"; + public static final String TYPE_REPORT = + "举报"; + public static final String TYPE_APPLY = + "申请"; + public static final String TYPE_OTHER = + "其他"; + public static final String TYPE_DELETE = + "删号"; + public static final String TYPE_PASSWORD = + "修改密码"; + + + + private static final Map + PRICES = new LinkedHashMap<>(); + static { + PRICES.put(TYPE_BUG, 10); + PRICES.put(TYPE_HELP, 5); + PRICES.put(TYPE_REPORT, 8); + PRICES.put(TYPE_APPLY, 15); + PRICES.put(TYPE_OTHER, 5); + PRICES.put(TYPE_DELETE, 0); + PRICES.put(TYPE_PASSWORD, 0); + } + + + public TicketManager(Main plugin) { + this.plugin = plugin; + } + + // ========== GUI ========== + + public void openMain(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_MAIN); + fillBg(g); + g.setItem(10, mk(Material.PAPER, + "§e创建工单", + "§7提交bug、求助、举报等")); + g.setItem(12, mk(Material.BOOK, + "§b我的工单", + "§7查看已提交的工单")); + g.setItem(14, mk(Material.CHEST, + "§6工单记录", + "§7查看历史工单")); + if (adm(p)) { + g.setItem(16, mk(Material.REDSTONE, + "§c工单管理", + "§7管理员专用")); + } + if (plugin.getDb() + .isServiceProvider( + p.getName())) { + g.setItem(18, mk(Material.EMERALD, + "§a服务商面板", + "§7接单、处理工单")); + } + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openCreate(Player p) { + Inventory g = Bukkit.createInventory( + null, 36, T_TICKET_CREATE); + fillBg(g); + g.setItem(10, mk(Material.BOOK, + "§cbug反馈")); + g.setItem(11, mk(Material.EMERALD, + "§a求助")); + g.setItem(12, mk(Material.BLAZE_POWDER, + "§6举报")); + g.setItem(13, mk(Material.NAME_TAG, + "§e申请")); + g.setItem(14, mk(Material.PAPER, + "§7其他")); + g.setItem(20, mk(Material.BARRIER, + "§c申请删号", + "§7申请删除账号")); + g.setItem(21, mk(Material.NETHER_WART, + "§d申请改密", + "§7请求管理员协助改密")); + g.setItem(31, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyTickets(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_MY); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByPlayer( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openDetail(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + Inventory g = Bukkit.createInventory( + null, 27, T_TICKET_DETAIL); + fillBg(g); + + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = sv(t, "requester"); + String title = sv(t, "title"); + String desc = sv(t, "description"); + String assigned = sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + int confirmed = nv(t, "admin_confirmed"); + + g.setItem(10, mk(Material.PAPER, + "§e§l" + (title.isEmpty() + ? type : title), + "§7类型: §f" + type, + "§7状态: " + stxt(status), + "§7提交者: §f" + requester, + "§7奖励: §e" + reward + " 积分")); + + g.setItem(12, mk(Material.BOOK, + "§7详细描述", + desc.isEmpty() ? "§7无描述" + : "§7" + (desc.length() > 40 + ? desc.substring(0, 40) + + "..." : desc))); + + if (!assigned.isEmpty()) { + g.setItem(14, mk(Material.PLAYER_HEAD, + "§7处理人: §f" + assigned, + confirmed == 1 + ? "§7评分: §e" + score + : "§7处理中")); + } else { + g.setItem(14, mk(Material.BARRIER, + "§7未分配")); + } + + // 驳回原因 + if ("closed".equals(status)) { + String reason = sv(t, "reject_reason"); + if (!reason.isEmpty()) { + g.setItem(13, mk(Material.BARRIER, + "§c驳回原因", + "§7" + reason)); + } + } + + // 回复按钮(非关闭/已完结状态) + if (!"closed".equals(status) + && !"withdrawn".equals(status) + && !"resolved".equals(status)) { + g.setItem(15, mk( + Material.WRITABLE_BOOK, + "§b回复工单", + "§7向对方发送消息")); + g.setItem(16, mk(Material.BOOK, + "§7查看回复记录")); + } + + // ===== 管理员按钮 ===== + if (adm(p)) { + if ("pending".equals(status)) { + // 待分配:可分配给服务商 + g.setItem(19, mk(Material.EMERALD, + "§a分配工单", + "§7指定服务商处理")); + // 也可自己处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && !assigned.equals("admin") + && !assigned.isEmpty()) { + // 服务商在处理:可取消指派收回 + g.setItem(19, mk(Material.BARRIER, + "§c取消指派", + "§7取消服务商指派,收回工单")); + // 也可自己接手处理 + g.setItem(20, mk(Material.LIME_DYE, + "§a我来处理", + "§7管理员直接处理此工单")); + } + if ("processing".equals(status) + && assigned.equals("admin")) { + // 管理员自己在处理:可标记完结 + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + if ("replied".equals(status) + && assigned.equals("admin")) { + g.setItem(19, mk(Material.LIME_DYE, + "§a标记完结", + "§7确认工单处理完成")); + } + // 驳回(非已关闭/已完结状态) + if (!"closed".equals(status) + && !"resolved".equals(status) + && !"withdrawn".equals(status)) { + g.setItem(21, mk(Material.RED_DYE, + "§c驳回工单", + "§7驳回并关闭此工单")); + } + } + + // ===== 服务商按钮 ===== + if (plugin.getDb() + .isServiceProvider(p.getName()) + && !TYPE_DELETE.equals(type) + && !TYPE_PASSWORD.equals(type)) { + if ("pending".equals(status) + && assigned.isEmpty()) { + // 待分配无人:抢单 + g.setItem(19, mk( + Material.GOLD_INGOT, + "§6抢单", + "§7接取此工单")); + } + if (assigned.equals(p.getName()) + && ("processing".equals(status) + || "replied".equals(status))) { + // 我在处理:标记完结/上报异常 + g.setItem(19, mk( + Material.LIME_DYE, + "§a标记完结", + "§7上报处理完成")); + g.setItem(20, mk( + Material.REDSTONE, + "§c上报异常", + "§7报告处理中的问题")); + } + } + + g.setItem(22, mk(Material.ARROW, + "§7返回")); + + ItemStack info = g.getItem(10); + if (info != null) { + ItemMeta im = info.getItemMeta(); + if (im != null) { + im.setCustomModelData(tid); + info.setItemMeta(im); + } + } + p.openInventory(g); + } + + + public void openAdminList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, T_TICKET_ADMIN); + fillBg(g); + g.setItem(45, mk(Material.EMERALD, + "§a邀请服务商")); + g.setItem(46, mk(Material.BARRIER, + "§c移除服务商")); + g.setItem(47, mk(Material.CLOCK, + "§e服务商列表")); + g.setItem(48, mk(Material.NETHER_STAR, + "§e直接派单", + "§7管理员直接创建并派单")); + List> all = + new ArrayList<>(); + all.addAll(plugin.getDb() + .getTicketsByStatus("pending")); + all.addAll(plugin.getDb() + .getTicketsByStatus("processing")); + all.addAll(plugin.getDb() + .getTicketsByStatus("replied")); + all.addAll(plugin.getDb() + .getTicketsByStatus("completed")); + all.addAll(plugin.getDb() + .getTicketsByStatus("resolved")); + all.addAll(plugin.getDb() + .getTicketsByStatus("closed")); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + + public void openProviderPanel(Player p) { + Inventory g = Bukkit.createInventory( + null, 27, "§d§l服务商面板"); + fillBg(g); + g.setItem(10, mk(Material.CHEST, + "§e抢单大厅", + "§7查看可接工单")); + g.setItem(12, mk(Material.CLOCK, + "§b我的工单", + "§7我正在处理的工单")); + g.setItem(14, mk(Material.PAPER, + "§6历史工单", + "§7已完成的工单")); + g.setItem(22, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openGrabList(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, "§6§l抢单大厅"); + fillBg(g); + List> pending = + plugin.getDb() + .getTicketsByStatus( + "pending"); + List> list = + new ArrayList<>(); + for (Map t : pending) { + if (sv(t, "assigned_to").isEmpty()) { + list.add(t); + } + } + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + public void openMyAssigned(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§b§l我处理中的工单"); + fillBg(g); + List> list = + plugin.getDb() + .getTicketsByProvider( + p.getName()); + fillList(g, list); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + // ========== 业务 ========== + + public int submitTicket(Player p, + String type, String title, + String desc) { + int price = PRICES + .getOrDefault(type, 5); + int id = plugin.getDb().createTicket( + type, "pending", "normal", + p.getName(), title, desc, + price, ""); + if (id > 0) { + if (TYPE_DELETE.equals(type) + || TYPE_PASSWORD.equals(type)) { + plugin.getDb().updateTicketField( + id, "assigned_to", "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage( + "§a§l[工单] §f工单 #" + + id + " 已提交," + + "等待管理员处理"); + } else { + p.sendMessage( + "§a§l[工单] §f创建成功!" + + " #§e" + id + + " 奖励: §e" + + price + "积分"); + } + notifyAdmins(p.getName(), id); + } else { + p.sendMessage("§c创建失败"); + } + return id; + } + + + public void adminCreateAndAssign( + Player p, String type, + String title, String desc, + String provider, int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + int id = plugin.getDb().createTicket( + type, "processing", "normal", + p.getName(), title, desc, + price, ""); + if (id <= 0) { + p.sendMessage("§c创建失败"); + return; + } + plugin.getDb().assignTicket( + id, provider); + if (price > 0) { + plugin.getDb().updateTicketField( + id, "reward_amount", price); + } + p.sendMessage("§a工单 #" + id + + " 已创建并分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f管理员指派工单 #" + + id + " 给您"); + playAssignedSound(tp); + } + } + + public void grabTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!"pending".equals( + sv(t, "status"))) { + p.sendMessage("§c该工单已被接单"); + return; + } + if (!sv(t, "assigned_to").isEmpty()) { + p.sendMessage("§c该工单已被分配"); + return; + } + plugin.getDb().assignTicket( + tid, p.getName()); + p.sendMessage("§a抢单成功!工单 #" + tid); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f您的工单 #" + + tid + " 已被 " + + p.getName() + + " 接单"); + } + } + + public void assignToProvider(Player p, + int tid, String provider, + int price) { + if (!plugin.getDb() + .isServiceProvider(provider)) { + p.sendMessage("§c" + provider + + " 不是服务商"); + return; + } + plugin.getDb().assignTicket( + tid, provider); + if (price > 0) { + plugin.getDb().updateTicketStatus( + tid, "processing"); + plugin.getDb().updateTicketField( + tid, "reward_amount", price); + } + p.sendMessage("§a工单 #" + tid + + " 已分配给 " + provider); + Player tp = Bukkit.getPlayer(provider); + if (tp != null && tp.isOnline()) { + tp.sendMessage( + "§e§l[工单] §f您有新工单: #" + + tid); + playAssignedSound(tp); + } + } + + public void confirmAndScore(Player p, + int tid, int score) { + if (score < 1 || score > 5) { + p.sendMessage("§c评分1-5"); + return; + } + plugin.getDb().confirmTicket( + tid, score); + p.sendMessage("§a工单 #" + tid + + " 已确认,评分: " + score); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已确认完结"); + } + } + settleTicket(tid); + } + + public void completeAsProvider(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "assigned_to") + .equals(p.getName())) { + p.sendMessage("§c这不是你的工单"); + return; + } + plugin.getDb().completeTicket(tid); + p.sendMessage("§a工单 #" + tid + + " 已标记完结,等待确认"); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 已处理完成"); + } + } + + public void rejectTicket(Player p, + int tid, String reason) { + plugin.getDb().updateTicketStatus( + tid, "closed"); + plugin.getDb().updateTicketField( + tid, "reject_reason", reason); + p.sendMessage("§c工单 #" + tid + + " 已驳回并关闭"); + Map t = + plugin.getDb().getTicket(tid); + if (t != null) { + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 被驳回: " + + reason); + } + } + } + + public void replyTicket(Player p, + int tid, String message) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + String status = sv(t, "status"); + if ("closed".equals(status) + || "withdrawn".equals(status) + || "resolved".equals(status)) { + p.sendMessage("§c该工单已关闭"); + return; + } + plugin.getDb().addTicketReply( + tid, p.getName(), message); + p.sendMessage("§a回复成功"); + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + String sender = p.getName(); + if (!requester.equals(sender)) { + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + if (!assigned.isEmpty() + && !assigned.equals(sender)) { + Player ap = Bukkit.getPlayer( + assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§e§l[工单] §f工单 #" + + tid + " 新回复"); + } + } + } + + public void showReplies(Player p, + int tid) { + List> replies = + plugin.getDb() + .getTicketReplies(tid); + if (replies.isEmpty()) { + p.sendMessage("§7暂无回复"); + return; + } + p.sendMessage("§e§l=== 回复记录 ==="); + SimpleDateFormat sdf = + new SimpleDateFormat( + "MM-dd HH:mm"); + for (Map r : replies) { + String sender = sv(r, "sender"); + String msg2 = sv(r, "message"); + long time = ((Number) r.get( + "create_time")).longValue(); + p.sendMessage("§7[" + sdf.format( + new Date(time)) + "] §f" + + sender + "§7: " + msg2); + } + p.sendMessage( + "§e§l================="); + } + + public void withdrawTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!sv(t, "requester") + .equals(p.getName()) + && !adm(p)) { + p.sendMessage("§c无权操作"); + return; + } + plugin.getDb().updateTicketStatus( + tid, "withdrawn"); + p.sendMessage("§a工单 #" + tid + + " 已撤回"); + } + + public void showProviderList(Player p) { + List> list = + plugin.getDb() + .getAllServiceProviders(); + if (list.isEmpty()) { + p.sendMessage("§7暂无服务商"); + return; + } + p.sendMessage( + "§e§l=== 服务商列表 ==="); + for (Map pro : list) { + p.sendMessage("§7- §f" + + pro.get("player_name") + + " §7角色: §e" + + pro.get("role")); + } + p.sendMessage( + "§e§l==================="); + } + + public void autoProcessCompleted() { + long timeoutMs = 10 * 60 * 1000L; + + // 自动处理删号工单(5分钟自动通过) + List> deleteTickets = + plugin.getDb() + .getTicketsByStatus( + "processing"); + for (Map t : deleteTickets) { + String dtype = sv(t, "type"); + if (!TYPE_DELETE.equals(dtype)) continue; + int did = nv(t, "id"); + int dconf = nv(t, "admin_confirmed"); + // 已经处理过了跳过 + if (dconf != 0) continue; + String dasg = sv(t, "assigned_to"); + // 只处理分配给admin的 + if (!"admin".equals(dasg)) continue; + long createTime = ((Number) t.get( + "create_time")).longValue(); + if (System.currentTimeMillis() + - createTime >= 5 * 60 * 1000L) { + processDeleteAccount(did, t); + } + } + + + // 已完结未确认→10分钟自动完结 + List> list = + plugin.getDb() + .getUnconfirmedCompleted( + timeoutMs); + for (Map t : list) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 已自动完结"); + String assigned = + sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) + pp.sendMessage( + "§e§l[工单] §f工单 #" + + id + + " 已自动完结"); + } + settleTicket(id); + } + + // 已回复但10分钟无互动→自动完结 + List> stale = + plugin.getDb() + .getRepliedStaleTickets( + timeoutMs); + for (Map t : stale) { + int id = nv(t, "id"); + plugin.getDb().confirmTicket(id, 3); + Player rp = Bukkit.getPlayer( + sv(t, "requester")); + if (rp != null && rp.isOnline()) + rp.sendMessage( + "§e§l[工单] §f工单 #" + + id + " 超时自动完结"); + settleTicket(id); + } + } + + + + // ========== GUI点击 ========== + public boolean handleClick(Player p, + String title, int slot) { + + if (title.equals(T_TICKET_MAIN)) { + if (slot == 10) openCreate(p); + else if (slot == 12) + openMyTickets(p); + else if (slot == 14) + openMyTickets(p); + else if (slot == 16) { + if (adm(p)) openAdminList(p); + else p.sendMessage("§c权限不足"); + } + else if (slot == 18) { + if (plugin.getDb() + .isServiceProvider(p.getName())) { + openProviderPanel(p); + } else { + p.sendMessage("§c权限不足"); + } + } + else if (slot == 22) + plugin.getGui().openMain(p); + return true; + } + + if (title.equals(T_TICKET_CREATE)) { + if (slot == 31) { + openMain(p); + return true; + } + String type = null; + if (slot == 10) type = TYPE_BUG; + else if (slot == 11) type = TYPE_HELP; + else if (slot == 12) type = TYPE_REPORT; + else if (slot == 13) type = TYPE_APPLY; + else if (slot == 14) type = TYPE_OTHER; + else if (slot == 20) type = TYPE_DELETE; + else if (slot == 21) type = TYPE_PASSWORD; + if (type != null) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_TITLE; + plugin.getChatInput() + .getState(p) + .ticketType = type; + if (TYPE_DELETE.equals(type)) { + p.sendMessage("§e请描述删号原因:"); + } else if (TYPE_PASSWORD.equals(type)) { + p.sendMessage("§e请描述需要协助的内容:"); + } else { + p.sendMessage("§e请输入工单标题:"); + } + } + return true; + } + + + if (title.equals(T_TICKET_MY)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals(T_TICKET_DETAIL)) { + return handleClickDetail(p, slot); + } + + if (title.equals(T_TICKET_ADMIN)) { + if (slot == 49) { + openMain(p); + return true; + } + if (slot == 45) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_INVITE_PROVIDER; + p.sendMessage("§e输入服务商玩家名:"); + return true; + } + if (slot == 46) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REMOVE_PROVIDER; + p.sendMessage("§e输入要移除的服务商:"); + return true; + } + if (slot == 47) { + showProviderList(p); + return true; + } + if (slot == 48) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ADMIN_TYPE; + p.sendMessage("§e选择工单类型:"); + p.sendMessage("§7bug/求助/举报/申请/其他"); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§d§l服务商面板")) { + if (slot == 22) { + plugin.getGui().openMain(p); + return true; + } + if (slot == 10) openGrabList(p); + else if (slot == 12) openMyAssigned(p); + else if (slot == 14) openMyTickets(p); + return true; + } + + if (title.equals("§6§l抢单大厅")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + if (title.equals("§b§l我处理中的工单")) { + if (slot == 49) { + openProviderPanel(p); + return true; + } + if (slot >= 0 && slot < 45) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(slot); + int id = gt(it); + if (id > 0) openDetail(p, id); + } + return true; + } + + return false; + } + + private boolean handleClickDetail(Player p, + int slot) { + if (slot == 22) { + openMyTickets(p); + return true; + } + int id = gtFromDetail(p); + if (id <= 0) return true; + Map t = + plugin.getDb().getTicket(id); + if (t == null) return true; + + String st = sv(t, "status"); + int conf = nv(t, "admin_confirmed"); + String asg = sv(t, "assigned_to"); + + // 回复按钮 + if (slot == 15) { + if (!"closed".equals(st) + && !"withdrawn".equals(st) + && !"resolved".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPLY; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入回复内容:"); + } + return true; + } + + // 查看回复记录 + if (slot == 16) { + showReplies(p, id); + return true; + } + + // slot 19 + if (slot == 19) { + // 管理员:分配工单 + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_ASSIGN; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage( + "§e输入: 服务商名 价格"); + return true; + } + // 管理员:取消指派(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + cancelTicket(p, id); + openDetail(p, id); + return true; + } + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + + // 管理员:标记完结(已回复状态) + if (adm(p) && "replied".equals(st) + && asg.equals("admin")) { + Map tk = + plugin.getDb().getTicket(id); + String tkType = sv(tk, "type"); + if (TYPE_DELETE.equals(tkType)) { + approveDeleteTicket(p, id); + return true; + } + plugin.getDb().completeTicket(id); + p.sendMessage("§a工单 #" + id + + " 已标记完结"); + openDetail(p, id); + return true; + } + } + // 服务商:抢单 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && "pending".equals(st) + && asg.isEmpty()) { + grabTicket(p, id); + openDetail(p, id); + return true; + } + // 服务商:标记完结 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + completeAsProvider(p, id); + openDetail(p, id); + return true; + } + return true; + } + + // slot 20 + if (slot == 20) { + // 管理员:自己处理(pending) + if (adm(p) && "pending".equals(st) + && asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + plugin.getDb().updateTicketStatus( + id, "processing"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 管理员:自己接手(服务商在处理中) + if (adm(p) && "processing".equals(st) + && !asg.equals("admin") + && !asg.isEmpty()) { + plugin.getDb().assignTicket( + id, "admin"); + p.sendMessage("§a工单 #" + id + + " 已由您接手处理"); + openDetail(p, id); + return true; + } + // 服务商:上报异常 + if (plugin.getDb() + .isServiceProvider(p.getName()) + && asg.equals(p.getName()) + && ("processing".equals(st) + || "replied".equals(st))) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REPORT_ISSUE; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入异常描述:"); + return true; + } + return true; + } + + // slot 21:驳回 + if (slot == 21) { + if (adm(p) && !"closed".equals(st) + && !"resolved".equals(st) + && !"withdrawn".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_REJECT; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage("§e输入驳回原因:"); + } + return true; + } + + return true; + } + + // ========== 聊天输入 ========== + + public boolean handleChatInput(Player p, + String msg, + ChatInputManager.InputType type) { + switch (type) { + case TICKET_TITLE: { + plugin.getChatInput() + .getState(p) + .ticketTitle = msg; + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_DESC; + p.sendMessage( + "§e请输入工单详细描述:"); + return true; + } + case TICKET_DESC: { + String tt = plugin.getChatInput() + .getState(p).ticketType; + String tl = plugin.getChatInput() + .getState(p).ticketTitle; + plugin.getChatInput().reset(p); + submitTicket(p, tt, tl, msg); + return true; + } + case TICKET_SCORE: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + try { + int sc = Integer.parseInt( + msg.trim()); + Map tk = + plugin.getDb() + .getTicket(tid2); + String st = tk != null + ? sv(tk, "status") + : ""; + if ("completed".equals(st)) { + plugin.getChatInput() + .reset(p); + confirmAndScore( + p, tid2, sc); + } else { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c工单尚未处理完成"); + } + } catch ( + NumberFormatException e) { + plugin.getChatInput() + .reset(p); + p.sendMessage( + "§c请输入数字1-5"); + } + return true; + } + case TICKET_REJECT: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + plugin.getChatInput().reset(p); + rejectTicket(p, tid2, msg); + return true; + } + case TICKET_ASSIGN: { + int tid2 = plugin.getChatInput() + .getState(p).ticketId; + String[] parts = msg.trim() + .split("\\s+"); + String prov = parts[0]; + int price = 0; + if (parts.length >= 2) { + try { + price = Integer.parseInt( + parts[1]); + } catch ( + NumberFormatException e) { + p.sendMessage( + "§c价格格式错误"); + } + } + plugin.getChatInput().reset(p); + assignToProvider(p, tid2, + prov, price); + runSync(() -> + openDetail(p, tid2)); + return true; + } + case TICKET_INVITE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .userExists(tgt)) { + p.sendMessage( + "§c玩家不存在"); + return true; + } + plugin.getDb() + .addServiceProvider( + tgt, "waiter"); + p.sendMessage("§a已邀请 " + tgt + + " 成为服务商"); + return true; + } + case TICKET_REMOVE_PROVIDER: { + String tgt = msg.trim(); + plugin.getChatInput().reset(p); + if (!plugin.getDb() + .isServiceProvider( + tgt)) { + p.sendMessage("§c" + tgt + + " 不是服务商"); + return true; + } + plugin.getDb() + .removeServiceProvider( + tgt); + p.sendMessage( + "§a已移除 " + tgt); + return true; + } + default: + return false; + } + } + + // ========== 私有 ========== + + private void settleTicket(int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) return; + String requester = + sv(t, "requester"); + String assigned = + sv(t, "assigned_to"); + int reward = nv(t, "reward_amount"); + int score = nv(t, "score"); + ConfigManager cfg = + plugin.getConfig2(); + if (reward > 0) { + plugin.getDb().addPoints( + requester, reward); + Player rp = Bukkit.getPlayer( + requester); + if (rp != null && rp.isOnline()) { + rp.sendMessage( + "§e§l[工单] §f获得 " + + reward + + " 积分奖励"); + } + } + if (!assigned.isEmpty()) { + int pts = (int) (score + * cfg.providerPointsPerScore); + if (pts > 0) { + plugin.getDb().addPoints( + assigned, pts); + } + double money = + cfg.baseEconomyReward + + score + * cfg.providerEconomyPerScore; + boolean ok = false; + try { + if (plugin.getEconomy() != null) { + OfflinePlayer op = + Bukkit.getOfflinePlayer( + assigned); + plugin.getEconomy() + .depositPlayer( + op, money); + ok = true; + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 经济失败: " + + e.getMessage()); + } + Player pp = + Bukkit.getPlayer(assigned); + if (pp != null && pp.isOnline()) { + pp.sendMessage( + "§a§l[工单] §f工单 #" + + tid + + " 确认完结"); + if (pts > 0) + pp.sendMessage("§7积分: §e" + + pts); + if (ok) + pp.sendMessage("§7经济: §a$" + + String.format( + "%.0f", money)); + } + } + } + + private void notifyAdmins(String creator, + int tid) { + String tag = + plugin.getConfig2().adminTag; + for (Player op : + Bukkit.getOnlinePlayers()) { + if (op.getScoreboardTags() + .contains(tag)) { + op.sendMessage( + "§e§l[工单] §f" + creator + + " 提交了新工单 #" + + tid); + } + } + } + + private void playAssignedSound(Player p) { + for (int i = 0; i < 3; i++) { + final int d = i * 15; + Bukkit.getScheduler() + .runTaskLater(plugin, () -> { + if (p.isOnline()) { + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_CHIME, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_BASS, + 1.0f, 0.79f); + p.playSound(p.getLocation(), + Sound + .BLOCK_NOTE_BLOCK_PLING, + 1.0f, 0.79f); + } + }, d); + } + } + + private void runSync(Runnable task) { + Bukkit.getScheduler() + .runTask(plugin, task); + } + + private boolean adm(Player p) { + return p.getScoreboardTags().contains( + plugin.getConfig2().adminTag); + } + + private void fillBg(Inventory g) { + ItemStack glass = mk( + Material.GRAY_STAINED_GLASS_PANE, + " "); + for (int i = 0; i < g.getSize(); i++) { + if (g.getItem(i) == null + || g.getItem(i).getType() + == Material.AIR) { + g.setItem(i, glass); + } + } + } + + private void fillList(Inventory g, + List> list) { + if (list.isEmpty()) { + g.setItem(22, mk(Material.BARRIER, + "§7暂无工单")); + return; + } + int slot = 0; + for (Map t : list) { + if (slot >= 45) break; + g.setItem(slot, makeItem(t)); + slot++; + } + } + /** + * 执行删号流程 + */ + /** + * 执行删号流程 + */ + public void processDeleteAccount(int tid, + Map t) { + String requester = sv(t, "requester"); + plugin.getDb().updateTicketStatus( + tid, "resolved"); + plugin.getDb().updateTicketField(tid, + "admin_confirmed", 1); + Player target = + Bukkit.getPlayer(requester); + if (target != null && target.isOnline()) { + plugin.backupInventory(target); + target.sendMessage( + "§a§l[工单] §f您的删号请求已通过"); + target.sendMessage( + "§7物品已备份,下次登录可取回"); + target.kickPlayer( + "§a删号成功,物品已备份。\n" + + "请等待5分钟后重新注册取回物品。"); + } else { + // 离线:仅标记,不操作数据库 + plugin.getLogger().info( + "[Sdf1_login] " + requester + + " 删号工单已通过,等待上线执行"); + } + // 延迟执行删号 + final String name = requester; + Bukkit.getScheduler().runTaskLater( + plugin, () -> { + plugin.getDb().deleteUser(name); + plugin.getLogger().info( + "[Sdf1_login] " + name + + " 删号工单已执行"); + }, 100L); + } + + /** + * 管理员手动批准删号 + */ + public void approveDeleteTicket(Player p, + int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + if (!TYPE_DELETE.equals(sv(t, "type"))) { + p.sendMessage("§c这不是删号工单"); + return; + } + p.sendMessage("§a工单 #" + tid + + " 已批准,正在执行删号..."); + processDeleteAccount(tid, t); + } + + /** + * 管理员取消工单(服务商异常后) + */ + /** + * 管理员取消工单(解除服务商绑定,管理员可自行处理) + */ + public void cancelTicket(Player p, int tid) { + Map t = + plugin.getDb().getTicket(tid); + if (t == null) { + p.sendMessage("§c工单不存在"); + return; + } + // 解除服务商绑定,状态回退到pending + plugin.getDb().updateTicketField( + tid, "assigned_to", ""); + plugin.getDb().updateTicketStatus( + tid, "pending"); + p.sendMessage("§a工单 #" + tid + + " 已取消指派,回到待分配状态"); + // 通知原服务商 + String assigned = sv(t, "assigned_to"); + if (!assigned.isEmpty()) { + Player ap = Bukkit.getPlayer(assigned); + if (ap != null && ap.isOnline()) { + ap.sendMessage( + "§c§l[工单] §f工单 #" + + tid + + " 已被管理员取消指派"); + } + } + } + + + /** + * 服务商查看历史工单 + */ + public void openProviderHistory(Player p) { + Inventory g = Bukkit.createInventory( + null, 54, + "§6§l我的历史工单"); + fillBg(g); + List> all = + plugin.getDb() + .getTicketsByProviderAll( + p.getName()); + fillList(g, all); + g.setItem(49, mk(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + private ItemStack makeItem( + Map t) { + int id = nv(t, "id"); + String type = sv(t, "type"); + String status = sv(t, "status"); + String requester = + sv(t, "requester"); + String title = sv(t, "title"); + int reward = nv(t, "reward_amount"); + Material mat; + switch (status) { + case "processing": + mat = Material.YELLOW_DYE; + break; + case "replied": + mat = Material.CYAN_DYE; + break; + case "completed": + mat = Material.LIME_DYE; + break; + case "resolved": + mat = Material.LIME_DYE; + break; + case "closed": + mat = Material.GRAY_DYE; + break; + case "rejected": + mat = Material.RED_DYE; + break; + case "withdrawn": + mat = Material.GRAY_DYE; + break; + default: + mat = Material.ORANGE_DYE; + break; + } + ItemStack item = new ItemStack(mat); + ItemMeta im = item.getItemMeta(); + if (im != null) { + im.setDisplayName("§e#" + id + + " " + (title.isEmpty() + ? type : title)); + List lore = + new ArrayList<>(); + lore.add("§7类型: §f" + type); + lore.add("§7状态: " + + stxt(status)); + lore.add("§7提交者: §f" + + requester); + lore.add("§7奖励: §e" + reward + + " 积分"); + lore.add(""); + lore.add("§7点击查看详情"); + im.setLore(lore); + im.setCustomModelData(id); + item.setItemMeta(im); + } + return item; + } + + private int gt(ItemStack item) { + if (item == null) return -1; + ItemMeta im = item.getItemMeta(); + if (im == null) return -1; + if (im.hasCustomModelData()) + return im.getCustomModelData(); + return -1; + } + + private int gtFromDetail(Player p) { + ItemStack it = p.getOpenInventory() + .getTopInventory().getItem(10); + return gt(it); + } + + private String stxt(String status) { + switch (status) { + case "pending": + return "§e待处理"; + case "processing": + return "§b处理中"; + case "replied": + return "§9已回复"; + case "completed": + return "§a待确认"; + case "resolved": + return "§a已完结"; + case "closed": + return "§7已关闭"; + case "rejected": + return "§c已驳回"; + case "withdrawn": + return "§7已撤回"; + default: + return "§7" + status; + } + } + + private String sv(Map m, + String key) { + Object v = m.get(key); + return v != null + ? String.valueOf(v) : ""; + } + + private int nv(Map m, + String key) { + Object v = m.get(key); + return v instanceof Number + ? ((Number) v).intValue() : 0; + } + + private ItemStack mk(Material mat, + String name, String... lore) { + ItemStack it = new ItemStack(mat); + ItemMeta im = it.getItemMeta(); + if (im != null) { + im.setDisplayName(name); + if (lore.length > 0) { + List list = + new ArrayList<>(); + for (String l : lore) { + if (!l.isEmpty()) + list.add(l); + } + if (!list.isEmpty()) + im.setLore(list); + } + it.setItemMeta(im); + } + return it; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/UpdateChecker.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/UpdateChecker.java new file mode 100644 index 0000000..c5c01bb --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/UpdateChecker.java @@ -0,0 +1,523 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; + +public class UpdateChecker { + + private final Main plugin; + private final String currentVersion; + + // ===== 仓库信息 ===== + private static final String GITHUB_OWNER = + "ypsdf1"; + private static final String GITHUB_REPO = + "sdf1_login"; + private static final String GITEE_OWNER = + "nihaoshidifu"; + private static final String GITEE_REPO = + "sdf1_login"; + + + // API 地址 + private static final String GITHUB_API = + "https://api.github.com/repos/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + + "/tags"; + private static final String GITEE_API = + "https://gitee.com/api/v5/repos/" + + GITEE_OWNER + "/" + + GITEE_REPO + + "/tags"; + + // 仓库页面链接(给用户看的) + private static final String GITHUB_LINK = + "https://github.com/" + + GITHUB_OWNER + "/" + + GITHUB_REPO + "/releases"; + private static final String GITEE_LINK = + "https://gitee.com/" + + GITEE_OWNER + "/" + + GITEE_REPO + "/releases"; + + + // 单次请求超时(毫秒) + private static final int TIMEOUT_MS = 3000; + + // 熔断阈值 + private static final int TRIP_THRESHOLD = 5; + + // ===== 熔断状态(内存态,重启自动清除) ===== + private volatile boolean circuitTripped = + false; + private volatile int failCount = 0; + + // ===== 自动检测禁用(持久化到文件) ===== + private volatile boolean autoCheckDisabled = + false; + private final File disabledFile; + + public UpdateChecker(Main plugin) { + this.plugin = plugin; + this.currentVersion = + plugin.getDescription() + .getVersion(); + this.disabledFile = new File( + plugin.getDataFolder(), + "update_disabled.dat"); + loadDisabledState(); + } + + // ========== 持久化禁用状态 ========== + + private void loadDisabledState() { + if (disabledFile.exists()) { + try (BufferedReader br = + new BufferedReader( + new FileReader( + disabledFile))) { + String line = br.readLine(); + if (line != null) { + autoCheckDisabled = + "true".equals( + line.trim()); + } + } catch (Exception ignored) { + } + } + } + + private void saveDisabledState() { + try (PrintWriter pw = + new PrintWriter( + new FileWriter( + disabledFile))) { + pw.println(autoCheckDisabled + ? "true" : "false"); + } catch (Exception ignored) { + } + } + + // ========== 公开方法 ========== + + /** + * onEnable 调用(异步静默) + * 熔断或禁用则直接跳过 + */ + public void checkOnEnable() { + if (autoCheckDisabled) return; + if (circuitTripped) return; + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> doCheck(null, true)); + } + + /** + * /sdf1_login update 调用(异步) + * 忽略熔断和禁用状态,强制检测 + * 只有拿到版号才解除熔断 + */ + public void checkUpdate( + CommandSender sender) { + Bukkit.getScheduler() + .runTaskAsynchronously(plugin, + () -> + doCheck(sender, + false)); + } + + // ========== 核心检测逻辑 ========== + private void doCheck(CommandSender sender, + boolean isAuto) { + + if (isAuto) { + if (autoCheckDisabled + || circuitTripped) { + return; + } + } + + // 通道1:GitHub + CheckResult gh = + tryChannel(GITHUB_API, + "GitHub", GITHUB_LINK); + + // 通道2:Gitee + CheckResult ge = + tryChannel(GITEE_API, + "Gitee", GITEE_LINK); + + // 选择成功的结果 + CheckResult chosen = null; + String failMsg = null; + + if (gh.isSuccess()) { + chosen = gh; + } else if (ge.isSuccess()) { + chosen = ge; + } else { + // 双通道均失败 + failCount++; + // 拼接两个通道的失败原因 + StringBuilder sb = + new StringBuilder(); + if (gh.error != null) { + sb.append("§7GitHub: §c") + .append(gh.error); + } + if (ge.error != null) { + if (sb.length() > 0) + sb.append("\n"); + sb.append("§7Gitee: §c") + .append(ge.error); + } + if (failCount >= TRIP_THRESHOLD) { + circuitTripped = true; + plugin.getLogger().warning( + "[Sdf1_login] 更新检测" + + "已熔断,连续失败" + + failCount + "次"); + } + if (sender != null) { + final String msg = + sb.toString(); + final boolean tripped = + circuitTripped; + final int fc = failCount; + Bukkit.getScheduler() + .runTask(plugin, () -> { + sender.sendMessage( + "§c§l[更新] " + + "检测失败"); + sender.sendMessage(msg); + sender.sendMessage( + "§e失败次数: " + + fc + "/" + + TRIP_THRESHOLD); + if (tripped) { + sender.sendMessage( + "§c已熔断," + + "请重启服务器" + + "重置"); + } + }); + } + return; + } + + // 至少一个通道成功 + failCount = 0; + + // 检查禁用信号 + if (isDisableSignal(chosen.version)) { + autoCheckDisabled = true; + saveDisabledState(); + plugin.getLogger().info( + "[Sdf1_login] 远程版本信号" + + " \"" + chosen.version + + "\",自动更新检测已禁用"); + if (sender != null) { + Bukkit.getScheduler() + .runTask(plugin, () -> + sender.sendMessage( + "§e[更新] 远程已" + + "禁用自动" + + "更新检测")); + } + return; + } + + // 手动调用:成功才解除熔断和禁用 + if (!isAuto) { + if (circuitTripped) { + circuitTripped = false; + } + if (autoCheckDisabled) { + autoCheckDisabled = false; + saveDisabledState(); + } + } + + final String src = chosen.source; + final String lnk = chosen.link; + final String rv = chosen.version; + + Bukkit.getScheduler() + .runTask(plugin, () -> { + if (sender == null) return; + + sender.sendMessage( + "§e[更新] 检测成功" + + " via: §a" + src); + sender.sendMessage( + "§e[更新] 本地版本:" + + " §f" + + currentVersion); + sender.sendMessage( + "§e[更新] 远程版本:" + + " §f" + rv); + + if (isNewer(rv, + currentVersion)) { + sender.sendMessage( + "§c[更新] 发现新" + + "版本: " + rv); + sender.sendMessage( + "§a请前往仓库下载" + + "更新:"); + sender.sendMessage( + "§a" + lnk); + } else { + sender.sendMessage( + "§a[更新] 已是" + + "最新版本"); + } + + // 显示另一通道的失败原因 + // (仅信息,不影响结果) + CheckResult other = + (src.equals("GitHub")) + ? ge : gh; + if (!other.isSuccess() + && other.error != null) { + sender.sendMessage( + "§7[更新] " + + other.source + + "不可用: " + + other.error); + } + }); + } + + + // ========== 禁用信号判断 ========== + + private boolean isDisableSignal( + String version) { + if (version == null) return false; + String v = version.trim().toLowerCase(); + return "999".equals(v) + || "关闭".equals(v) + || "false".equals(v) + || "禁用".equals(v); + } + + // ========== HTTP 请求(带超时) ========== + + // ========== 检测结果 ========== + + private static class CheckResult { + final String version; + final String source; + final String link; + final String error; + + CheckResult(String version, + String source, + String link, + String error) { + this.version = version; + this.source = source; + this.link = link; + this.error = error; + } + + boolean isSuccess() { + return version != null; + } + } + +// ========== HTTP 请求 ========== + + private CheckResult tryChannel( + String apiUrl, + String sourceName, + String link) { + HttpURLConnection conn = null; + try { + URL url = new URL(apiUrl); + conn = (HttpURLConnection) + url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty( + "User-Agent", + "Mozilla/5.0 (Windows NT 10.0; " + + "Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) " + + "Chrome/120.0.0.0 Safari/537.36"); + conn.setRequestProperty( + "Accept", + "*/*"); + conn.setRequestProperty( + "Accept-Language", + "zh-CN,zh;q=0.9"); + conn.setConnectTimeout(TIMEOUT_MS); + conn.setReadTimeout(TIMEOUT_MS); + conn.setInstanceFollowRedirects(true); + + int code = conn.getResponseCode(); + + if (code == 404) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 未找到 (HTTP 404)"); + } + if (code == 403) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 被限流 (HTTP 403)"); + } + if (code != 200) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": HTTP " + code); + } + + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn + .getInputStream())); + StringBuilder sb = + new StringBuilder(); + String line; + while ((line = reader.readLine()) + != null) { + sb.append(line); + } + reader.close(); + + String tag = + parseTagName(sb.toString()); + if (tag == null) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 无法解析版本号"); + } + + return new CheckResult(tag, + sourceName, link, null); + + } catch (java.net + .SocketTimeoutException e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": 连接超时"); + } catch (java.net.ConnectException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": 连接被拒绝"); + } catch (java.net + .UnknownHostException e) { + return new CheckResult(null, + sourceName, link, + sourceName + + ": DNS解析失败"); + } catch (Exception e) { + return new CheckResult(null, + sourceName, link, + sourceName + ": " + + e.getMessage()); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + + + /** + * 从 JSON 中提取 tag_name + */ + private String parseTagName(String json) { + // tags API 返回数组: [{"name":"v1.1",...}] + // releases API 返回对象: {"tag_name":"v1.1"} + + // 尝试 tag_name(releases格式) + int idx = + json.indexOf("\"tag_name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + // 尝试 "name"(tags格式) + idx = json.indexOf("\"name\""); + if (idx >= 0) { + int colon = + json.indexOf(":", idx); + int q1 = + json.indexOf("\"", colon + 1); + int q2 = + json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag( + json.substring( + q1 + 1, q2)); + } + } + + return null; + } + + private String cleanTag(String tag) { + if (tag.startsWith("v") + || tag.startsWith("V")) { + tag = tag.substring(1); + } + return tag.isEmpty() ? null : tag; + } + + + // ========== 版本比较 ========== + + private boolean isNewer(String remote, + String local) { + String[] r = remote.split("\\."); + String[] l = local.split("\\."); + int len = Math.max(r.length, l.length); + for (int i = 0; i < len; i++) { + int ri = i < r.length + ? parseNum(r[i]) : 0; + int li = i < l.length + ? parseNum(l[i]) : 0; + if (ri > li) return true; + if (ri < li) return false; + } + return false; + } + + private int parseNum(String s) { + try { + return Integer.parseInt( + s.replaceAll("[^0-9]", "")); + } catch (Exception e) { + return 0; + } + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/UserGroupManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/UserGroupManager.java new file mode 100644 index 0000000..adaf907 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/UserGroupManager.java @@ -0,0 +1,370 @@ +package Sdf1_login; + +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.util.*; +import java.util.Base64; +import java.util.concurrent.ConcurrentHashMap; + +public class UserGroupManager { + + private final Main plugin; + private Connection db; + private final Map groupConfigs = new ConcurrentHashMap<>(); + + public UserGroupManager(Main plugin) { + this.plugin = plugin; + initDB(); + loadGroupConfigs(); + startExpiryChecker(); + } + + // ===== DB ===== + + private void initDB() { + try { + Class.forName("org.sqlite.JDBC"); + File dbFile = new File(plugin.getDataFolder(), "usergroup.db"); + db = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath()); + Statement st = db.createStatement(); + st.execute("PRAGMA journal_mode=WAL"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_config (" + + "group_name TEXT PRIMARY KEY," + + "display_name TEXT DEFAULT ''," + + "display_color TEXT DEFAULT '§f'," + + "display_emoji TEXT DEFAULT ''," + + "priority INTEGER DEFAULT 0," + + "is_permanent INTEGER DEFAULT 1," + + "duration_minutes INTEGER DEFAULT 0," + + "pack_url TEXT DEFAULT ''," + + "pack_hash TEXT DEFAULT ''," + + "pack_prompt TEXT DEFAULT 'Custom Resource Pack'," + + "pack_size INTEGER DEFAULT 0)"); + st.execute("CREATE TABLE IF NOT EXISTS user_group_member (" + + "player_name TEXT NOT NULL," + + "group_name TEXT NOT NULL," + + "added_by TEXT DEFAULT 'system'," + + "added_time INTEGER DEFAULT 0," + + "expiry_time INTEGER DEFAULT 0," + + "PRIMARY KEY(player_name, group_name))"); + st.close(); + } catch (Exception e) { + throw new RuntimeException("[UserGroup] DB init failed: " + e.getMessage(), e); + } + } + + // ===== 配置加载 ===== + + public void loadGroupConfigs() { + groupConfigs.clear(); + File dir = new File(plugin.getDataFolder(), "用户组"); + if (!dir.exists()) dir.mkdirs(); + File[] files = dir.listFiles((d, n) -> n.endsWith(".txt")); + if (files == null) return; + + for (File f : files) { + try { + List lines = java.nio.file.Files.readAllLines( + f.toPath(), StandardCharsets.UTF_8); + UserGroupConfig cfg = parseGroupConfig(lines); + if (cfg != null) { + groupConfigs.put(cfg.name, cfg); + saveGroupConfigToDB(cfg); + } + } catch (Exception e) { + plugin.getLogger().warning("[UserGroup] Failed to load: " + f.getName()); + } + } + } + + private UserGroupConfig parseGroupConfig(List lines) { + UserGroupConfig cfg = new UserGroupConfig(); + boolean inBlock = false; + + for (String raw : lines) { + String l = raw; + + // 块注释 + if (l.contains("")) { inBlock = false; continue; } + if (l.contains("/*")) { inBlock = true; continue; } + if (l.contains("*/")) { inBlock = false; continue; } + if (inBlock) continue; + + // 行注释 + l = l.trim(); + if (l.isEmpty()) continue; + if (l.startsWith("#") || l.startsWith("//")) continue; + + // 行内注释 + int h = l.indexOf('#'); + int s = l.indexOf("//"); + if (h >= 0) l = l.substring(0, h).trim(); + if (s >= 0 && (h < 0 || s < h)) l = l.substring(0, s).trim(); + if (l.isEmpty()) continue; + + // 解析键值对 + String key = l; + String val = ""; + int idx = l.indexOf('\uff1a'); + if (idx < 0) idx = l.indexOf(':'); + if (idx >= 0) { + key = l.substring(0, idx).trim(); + val = l.substring(idx + 1).trim(); + if (val.startsWith("\"") && val.endsWith("\"")) + val = val.substring(1, val.length() - 1); + } + + switch (key) { + case "组名": + case "name": cfg.name = val; break; + case "显示名": + case "display": cfg.displayName = val; break; + case "颜色": + case "color": cfg.displayColor = val; break; + case "符号": + case "emoji": cfg.displayEmoji = val; break; + case "优先级": + case "priority": + try { cfg.priority = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "类型": + case "type": + cfg.permanent = val.equalsIgnoreCase("永久") || val.equalsIgnoreCase("permanent"); + break; + case "时长": + case "duration": + try { cfg.durationMinutes = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "资源包URL": + case "pack_url": cfg.packUrl = val; break; + case "资源包Hash": + case "pack_hash": cfg.packHash = val; break; + case "资源包提示": + case "pack_prompt": cfg.packPrompt = val; break; + case "资源包大小": + case "pack_size": + try { cfg.packSize = Integer.parseInt(val); } catch (Exception ignored) {} + break; + case "本地文件": + case "local_file": cfg.localFile = val; break; + } + } + + if (cfg.name == null || cfg.name.isEmpty()) return null; + return cfg; + } + + private void saveGroupConfigToDB(UserGroupConfig cfg) { + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_config " + + "(group_name, display_name, display_color, display_emoji," + + " priority, is_permanent, duration_minutes," + + " pack_url, pack_hash, pack_prompt, pack_size)" + + " VALUES (?,?,?,?,?,?,?,?,?,?,?)"); + ps.setString(1, cfg.name); + ps.setString(2, cfg.displayName); + ps.setString(3, cfg.displayColor); + ps.setString(4, cfg.displayEmoji); + ps.setInt(5, cfg.priority); + ps.setInt(6, cfg.permanent ? 1 : 0); + ps.setInt(7, cfg.durationMinutes); + ps.setString(8, cfg.packUrl); + ps.setString(9, cfg.packHash); + ps.setString(10, cfg.packPrompt); + ps.setInt(11, cfg.packSize); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 玩家操作 ===== + + public boolean addPlayer(String player, String groupName, String addedBy, long durationMinutes) { + UserGroupConfig cfg = groupConfigs.get(groupName); + if (cfg == null) return false; + long now = System.currentTimeMillis(); + long expiry = cfg.permanent ? 0 : now + durationMinutes * 60 * 1000; + if (!cfg.permanent && durationMinutes > 0) expiry = now + durationMinutes * 60 * 1000; + else if (!cfg.permanent) expiry = now + cfg.durationMinutes * 60 * 1000; + try { + PreparedStatement ps = db.prepareStatement( + "INSERT OR REPLACE INTO user_group_member " + + "(player_name, group_name, added_by, added_time, expiry_time)" + + " VALUES (?,?,?,?,?)"); + ps.setString(1, player); + ps.setString(2, groupName); + ps.setString(3, addedBy); + ps.setLong(4, now); + ps.setLong(5, expiry); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + Player p = plugin.getServer().getPlayerExact(player); + if (p != null) sendResourcePack(p); + return true; + } + + public boolean removePlayer(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + int rows = ps.executeUpdate(); + ps.close(); + return rows > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public List> getPlayerGroups(String player) { + List> list = new ArrayList<>(); + try { + PreparedStatement ps = db.prepareStatement( + "SELECT * FROM user_group_member WHERE player_name=?"); + ps.setString(1, player); + ResultSet rs = ps.executeQuery(); + long now = System.currentTimeMillis(); + while (rs.next()) { + Map row = new LinkedHashMap<>(); + row.put("group_name", rs.getString("group_name")); + row.put("added_by", rs.getString("added_by")); + long expiry = rs.getLong("expiry_time"); + row.put("expiry_time", expiry); + row.put("expired", expiry > 0 && expiry <= now); + list.add(row); + } + rs.close(); ps.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + return list; + } + + public UserGroupConfig getHighestGroup(String player) { + List> groups = getPlayerGroups(player); + long now = System.currentTimeMillis(); + UserGroupConfig best = null; + for (Map g : groups) { + long expiry = (long) g.get("expiry_time"); + if (expiry > 0 && expiry <= now) continue; + String name = (String) g.get("group_name"); + UserGroupConfig cfg = groupConfigs.get(name); + if (cfg != null && (best == null || cfg.priority > best.priority)) { + best = cfg; + } + } + return best; + } + + public boolean isPlayerInGroup(String player, String groupName) { + try { + PreparedStatement ps = db.prepareStatement( + "SELECT 1 FROM user_group_member WHERE player_name=? AND group_name=?"); + ps.setString(1, player); + ps.setString(2, groupName); + boolean exists = ps.executeQuery().next(); + ps.close(); + return exists; + } catch (SQLException e) { + return false; + } + } + + // ===== 过期自动移除 ===== + + private void startExpiryChecker() { + new BukkitRunnable() { + @Override + public void run() { + checkExpiry(); + } + }.runTaskTimerAsynchronously(plugin, 1200L, 1200L); // 每分钟检查 + } + + private void checkExpiry() { + try { + long now = System.currentTimeMillis(); + PreparedStatement ps = db.prepareStatement( + "SELECT player_name, group_name FROM user_group_member " + + "WHERE expiry_time > 0 AND expiry_time <= ?"); + ps.setLong(1, now); + ResultSet rs = ps.executeQuery(); + List expired = new ArrayList<>(); + while (rs.next()) { + expired.add(new String[]{ + rs.getString("player_name"), + rs.getString("group_name")}); + } + rs.close(); ps.close(); + + for (String[] e : expired) { + PreparedStatement del = db.prepareStatement( + "DELETE FROM user_group_member WHERE player_name=? AND group_name=?"); + del.setString(1, e[0]); + del.setString(2, e[1]); + del.executeUpdate(); + del.close(); + plugin.getLogger().info("[UserGroup] 过期移除: " + e[0] + " -> " + e[1]); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + // ===== 资源包分发 ===== + + public void sendResourcePack(Player p) { + UserGroupConfig cfg = getHighestGroup(p.getName()); + if (cfg == null || cfg.packUrl == null || cfg.packUrl.isEmpty()) return; + + String url = cfg.packUrl; + String prompt = cfg.packPrompt != null + ? cfg.packPrompt : "Custom Resource Pack"; + + plugin.getServer().getScheduler().runTaskLater(plugin, () -> { + try { + p.setResourcePack(url, new byte[0], prompt, false); + plugin.getLogger().info("[UserGroup] 资源包已发送: " + + p.getName() + " -> " + cfg.displayName); + } catch (Exception e) { + plugin.getLogger().warning( + "[UserGroup] 资源包发送失败: " + e.getMessage()); + } + }, 40L); + } + + public Map getGroupConfigs() { + return groupConfigs; + } + + // ===== 配置类 ===== + + public static class UserGroupConfig { + public String name; + public String displayName = ""; + public String displayColor = "§f"; + public String displayEmoji = ""; + public int priority = 0; + public boolean permanent = true; + public int durationMinutes = 0; + public String packUrl = ""; + public String packHash = ""; + public String packPrompt = "Custom Resource Pack"; + public int packSize = 0; + public String localFile = ""; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/VerificationManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/VerificationManager.java new file mode 100644 index 0000000..3fc4ceb --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/VerificationManager.java @@ -0,0 +1,158 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.function.Consumer; + +public class VerificationManager { + + private final Main plugin; + + public VerificationManager(Main plugin) { + this.plugin = plugin; + } + + public boolean isBedrockPlayer(Player p) { + String uuid = p.getUniqueId().toString(); + return uuid.contains("f851ba4") + || uuid.startsWith("00000000-0000-0000"); + } + + public boolean isOnlineMode() { + return Bukkit.getServer().getOnlineMode(); + } + + public void verifyPremiumAsync(Player player, + Consumer callback) { + Thread t = new Thread(() -> doVerify(player, callback), + "Sdf1_login-PremiumVerify"); + t.setDaemon(true); + t.start(); + } + + private void doVerify(Player player, + Consumer callback) { + String name = player.getName(); + String uuid = player.getUniqueId().toString(); + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证开始 ======"); + plugin.getLogger().info( + "[Sdf1_login] 玩家: " + name); + plugin.getLogger().info( + "[Sdf1_login] 当前UUID: " + uuid); + + try { + UUID offlineUuid = UUID.nameUUIDFromBytes( + ("OfflinePlayer:" + name) + .getBytes(StandardCharsets.UTF_8)); + String offlineStr = offlineUuid.toString(); + + plugin.getLogger().info( + "[Sdf1_login] 离线UUID: " + offlineStr); + + boolean isOfflineUuid = + player.getUniqueId().equals(offlineUuid); + + plugin.getLogger().info( + "[Sdf1_login] UUID匹配离线格式: " + + isOfflineUuid); + + if (!isOfflineUuid) { + plugin.getLogger().info( + "[Sdf1_login] 结论: 正版玩家"); + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(true); + return; + } + + plugin.getLogger().info( + "[Sdf1_login] UUID为离线格式," + + "查询Mojang API..."); + UUID mojangUuid = fetchMojangUuid(name); + + if (mojangUuid != null) { + plugin.getLogger().info( + "[Sdf1_login] Mojang返回UUID: " + + mojangUuid); + plugin.getLogger().info( + "[Sdf1_login] 结论: 盗版客户端" + + "使用了正版名字"); + } else { + plugin.getLogger().info( + "[Sdf1_login] Mojang无此名字"); + plugin.getLogger().info( + "[Sdf1_login] 结论: 纯离线玩家"); + } + + plugin.getLogger().info( + "[Sdf1_login] ====== 验证结束 ======"); + callback.accept(false); + + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] 验证异常: " + + e.getMessage()); + callback.accept(false); + } + } + + private UUID fetchMojangUuid(String name) { + HttpURLConnection conn = null; + try { + URL url = new URL( + "https://api.mojang.com/users/profiles/minecraft/" + + name); + conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + conn.setRequestMethod("GET"); + + int code = conn.getResponseCode(); + plugin.getLogger().info( + "[Sdf1_login] Mojang API响应: " + code); + + if (code == 200) { + BufferedReader reader = + new BufferedReader( + new InputStreamReader( + conn.getInputStream(), + StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + + String json = sb.toString(); + String uuidStr = json.replaceAll( + ".*\"id\"\\s*:\\s*\"([^\"]+)\".*", + "$1"); + plugin.getLogger().info( + "[Sdf1_login] Mojang原始UUID: " + + uuidStr); + + String formatted = uuidStr.replaceFirst( + "(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", + "$1-$2-$3-$4-$5"); + return UUID.fromString(formatted); + } + } catch (Exception e) { + plugin.getLogger().warning( + "[Sdf1_login] Mojang查询异常: " + + e.getMessage()); + } finally { + if (conn != null) conn.disconnect(); + } + return null; + } +} diff --git a/backup/src_backup_20260605_125051/main/java/Sdf1_login/WelcomeManager.java b/backup/src_backup_20260605_125051/main/java/Sdf1_login/WelcomeManager.java new file mode 100644 index 0000000..68bd3ef --- /dev/null +++ b/backup/src_backup_20260605_125051/main/java/Sdf1_login/WelcomeManager.java @@ -0,0 +1,163 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class WelcomeManager { + + private final Main plugin; + private final Random random = new Random(); + private final List registerMsgs = + new ArrayList<>(); + private final List loginMsgs = + new ArrayList<>(); + + public WelcomeManager(Main plugin) { + this.plugin = plugin; + loadMessages(); + } + + public void loadMessages() { + registerMsgs.clear(); + loginMsgs.clear(); + File f = new File( + plugin.getDataFolder(), + "欢迎仪式.txt"); + if (!f.exists()) { + writeDefault(f); + } + try (BufferedReader r = + new BufferedReader( + new InputStreamReader( + new FileInputStream(f), + StandardCharsets + .UTF_8))) { + String section = ""; + String line; + while ((line = r.readLine()) != null) { + if (line.startsWith("\uFEFF")) + line = line.substring(1); + String t = line.trim(); + if (t.isEmpty() + || t.startsWith("#")) + continue; + if (t.equals("注册") + || t.equals("注册:")) { + section = "register"; + continue; + } + if (t.equals("登录") + || t.equals("登录:")) { + section = "login"; + continue; + } + // 支持 = 和 : 两种分隔 + if (section.isEmpty()) { + if (t.startsWith("注册") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + registerMsgs.add( + colorize(v)); + continue; + } + if (t.startsWith("登录") + && (t.contains("=") + || t.contains(":"))) { + int idx = t.indexOf('='); + int idx2 = t.indexOf(':'); + if (idx < 0) idx = idx2; + else if (idx2 >= 0) + idx = Math.min(idx, idx2); + String v = t.substring(idx + 1) + .trim(); + if (!v.isEmpty()) + loginMsgs.add( + colorize(v)); + continue; + } + continue; + } + if (!t.isEmpty()) { + if (section.equals("register")) + registerMsgs.add( + colorize(t)); + else + loginMsgs.add( + colorize(t)); + } + } + } catch (Exception e) { + plugin.getLogger().severe( + "[欢迎] 加载失败: " + + e.getMessage()); + } + plugin.getLogger().info( + "[欢迎] 加载完成: 注册=" + + registerMsgs.size() + + " 登录=" + loginMsgs.size()); + } + + public void onRegister(Player p) { + sendRandom(p, registerMsgs, "注册"); + } + + public void onLogin(Player p) { + sendRandom(p, loginMsgs, "登录"); + } + + private void sendRandom(Player p, + List msgs, String tag) { + if (msgs.isEmpty()) return; + String msg = msgs.get( + random.nextInt(msgs.size())); + msg = msg.replace("{player}", + p.getName()); + final String finalMsg = msg; + Bukkit.getScheduler() + .runTaskLater(plugin, + () -> Bukkit + .broadcastMessage( + finalMsg), 10L); + } + + private String colorize(String msg) { + return msg.replace("&", "\u00a7"); + } + + private void writeDefault(File f) { + try (PrintWriter pw = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(f), + StandardCharsets + .UTF_8))) { + pw.println("# 欢迎仪式配置"); + pw.println("# &为颜色代码"); + pw.println("# {player} = 玩家名"); + pw.println(); + pw.println("注册:"); + pw.println("&a&l欢迎 &f{player} &a加入服务器!&7[注册]"); + pw.println("&6&l{player} &e已完成注册,欢迎入驻!&7[注册]"); + pw.println("&b&l新成员 &f{player} &b已就位!&7[注册]"); + pw.println(); + pw.println("登录:"); + pw.println("&a&l欢迎回来 &f{player}&a!&7[登录]"); + pw.println("&e&l{player} &e已登录,继续冒险吧!&7[登录]"); + pw.println("&b&l{player} &b回来了!&7[登录]"); + } catch (IOException ignored) { + } + } +} diff --git a/backup/src_backup_20260605_125051/main/resources/plugin.yml b/backup/src_backup_20260605_125051/main/resources/plugin.yml new file mode 100644 index 0000000..f6868d9 --- /dev/null +++ b/backup/src_backup_20260605_125051/main/resources/plugin.yml @@ -0,0 +1,104 @@ +name: Sdf1_login +version: 1.0 +main: Sdf1_login.Main +api-version: '1.21' +commands: + sdf1_login: + description: Sdf1_login主命令 + usage: /sdf1_login + reg: + description: 注册 + usage: /reg <密码> + login: + description: 登录 + usage: /login <密码> + l: + description: 登录快捷版 + usage: /l <密码> + 注册: + description: 注册(中文) + usage: /注册 <密码> + 登录: + description: 登录(中文) + usage: /登录 <密码> + 玩家信息: + description: 打开面板(中文) + usage: /玩家信息 + 改密码: + description: 修改密码(中文) + usage: /改密码 <旧密码> <新密码> <确认新密码> + 设置邮箱: + description: 设置邮箱(中文) + usage: /设置邮箱 <邮箱地址> + 撤销: + description: 撤销操作(中文) + usage: /撤销 + 删除: + description: 删除玩家(中文) + usage: /删除 <玩家> <密码> + 找回密码: + description: 找回密码(中文) + usage: /找回密码 + 签到: + description: 每日签到(中文) + usage: /签到 + #oa: + # description: 申请删除账号 + # usage: /oa [玩家名] + 删除账号: + description: 申请删除账号(中文) + usage: /删除账号 [玩家名] + 通过: + description: 批准OA请求(中文) + usage: /通过 <编号> + 拒绝: + description: 拒绝OA请求(中文) + usage: /拒绝 <编号> [理由] + accept: + description: 批准OA请求 + usage: /accept <编号> + deny: + description: 拒绝OA请求 + usage: /deny <编号> [理由] + email: + description: 绑定邮箱 + usage: /email <邮箱> + 绑定: + description: 绑定邮箱(中文) + usage: /绑定 <邮箱> + 绑定邮箱: + description: 绑定邮箱 + usage: /绑定邮箱 + chat: + description: 聊天过滤管理 + usage: /chat + recycle: + description: 打开垃圾回收站 + usage: /recycle + 垃圾清理: + description: 手动清理掉落物 + usage: /垃圾清理 + menu: + description: 雪球菜单开关控制 + usage: /menu <开/关> + pvp : + description: PVP榜单 + usage: /pvp + cypay : + description: 草原探险债券 + usage: /cypay + printer : + description: 打印债券流水 + usage: /printer + shop : + description: 游戏商人 + usage: /shop + 商店: + description: 打开商店 + usage: /商店 + #testorder: + # description: 测试订单中心 + # usage: /testorder + protect: + description: 区域保护 + usage: /protect \ No newline at end of file diff --git a/src/main/java/Sdf1_login/AreaProtection.java b/src/main/java/Sdf1_login/AreaProtection.java index 91f3f90..5b4992f 100644 --- a/src/main/java/Sdf1_login/AreaProtection.java +++ b/src/main/java/Sdf1_login/AreaProtection.java @@ -21,6 +21,11 @@ import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.entity.Entity; import org.bukkit.event.player.PlayerInteractAtEntityEvent; import java.sql.*; +import org.bukkit.event.entity.*; +import org.bukkit.event.player.*; +import org.bukkit.GameMode; +import org.bukkit.event.EventPriority; + import java.io.*; import java.nio.charset.StandardCharsets; @@ -30,21 +35,20 @@ import org.bukkit.Bukkit; import java.util.Iterator; import java.util.Set; import java.util.HashSet; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.entity.Item; + public class AreaProtection implements Listener { private final Main plugin; - private final Map lastLeaveTime - = new HashMap<>(); - private final Map> pendingClear - = new HashMap<>(); - private final Map> activeEffects - = new HashMap<>(); + public List giveEffects = new ArrayList<>(); private final Map> playerAreas = new HashMap<>(); - private final File areaDir; private final Map areas = new ConcurrentHashMap<>(); private Connection dbConnection; @@ -72,6 +76,12 @@ public class AreaProtection implements Listener { private final Set selecting = ConcurrentHashMap.newKeySet(); public boolean denyRaid = false; + // ===== 统一文件夹 ===== + private final File rootDir; + private final File whitelistDir; + private final File globalWhitelistFile; + private final File oldDbFile; + private final File newDbFile; // 边框显示 private final Map displayTaskIds = @@ -82,20 +92,410 @@ public class AreaProtection implements Listener { = new HashMap<>(); private final Map protectedEntities = new ConcurrentHashMap<>(); - // 记录每个玩家当前所在的区域名 - private final Map playerCurrentArea - = new HashMap<>(); + // 区域给予的效果记录(贴标) + private final Map> playerAppliedEffects = new HashMap<>(); + // 延时清理任务 + private static final long MOVE_DEBOUNCE_MS = 100; + // 新:全部改为线程安全 + private final Map playerCurrentArea + = new ConcurrentHashMap<>(); + private final Map> playerMarkedEffects + = new ConcurrentHashMap<>(); + private final Map pendingClearTask + = new ConcurrentHashMap<>(); + private final Map pendingClearArea + = new ConcurrentHashMap<>(); + private final Map lastMoveProcess + = new ConcurrentHashMap<>(); + // 防止传送时重复处理 + private final Set teleporting + = ConcurrentHashMap.newKeySet(); + // 待清理的效果列表(与延时任务配合) + private final Map> pendingClearEffects + = new ConcurrentHashMap<>(); + +// ==================== 强制游戏模式 ==================== + + /** + * 监听玩家尝试切换游戏模式 + * 如果在强制游戏模式区域且不在豁免名单中,则取消切换 + */ + // 防止递归的标记 + private final Set modeSwitching + = ConcurrentHashMap.newKeySet(); + @EventHandler(priority = EventPriority.HIGHEST) + public void onGameModeChange( + PlayerGameModeChangeEvent event) { + if (event.isCancelled()) return; + + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + if (modeSwitching.contains(uid)) return; + + String areaName = playerCurrentArea.get(uid); + if (areaName == null) return; + + AreaConfig ac = areas.get(areaName); + if (ac == null) return; + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) return; + if (isExemptFromGameMode(p, areaName)) return; + + event.setCancelled(true); + + modeSwitching.add(uid); + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } finally { + modeSwitching.remove(uid); + } + + // ★ 切换后发一次中文提示 + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + ac.enforceGameMode + + " 模式,无法切换")); + } + + public Set getAlreadyForced() { + return alreadyForced; + } + + + // ==================== 定时强制游戏模式 ==================== + + private BukkitTask enforceTask; + + /** + * 启动定时强制任务 + * 每秒检查一次所有在线玩家 + */ + private BukkitTask enforceTask1; + + // 记录上次强制模式的玩家(避免重复发消息) + private final Set alreadyForced + = ConcurrentHashMap.newKeySet(); + + public void startEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + } + enforceTask = new BukkitRunnable() { + @Override + public void run() { + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String areaName = + playerCurrentArea.get(uid); + if (areaName == null) { + // 不在任何区域,清除标记 + alreadyForced.remove(uid); + continue; + } + + AreaConfig ac = areas.get(areaName); + if (ac == null) continue; + if (ac.enforceGameMode == null + || ac.enforceGameMode + .isEmpty()) continue; + if (isExemptFromGameMode( + p, areaName)) { + alreadyForced.remove(uid); + continue; + } + + GameMode target = + GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + modeSwitching.add(uid); + try { + p.setGameMode(target); + } finally { + modeSwitching.remove(uid); + } + + // ★ 只在玩家已被强制过时才发消息 + // 首次不发,后续才提醒 + if (alreadyForced.contains(uid)) { + p.sendMessage(formatAreaMsg( + "&c该区域强制 " + + ac.enforceGameMode + + " 模式,无法切换")); + } + alreadyForced.add(uid); + } else { + // 模式一致,清除标记 + alreadyForced.remove(uid); + } + } + } + }.runTaskTimer(plugin, 20L, 20L); + } + + public void stopEnforceTask() { + if (enforceTask != null) { + enforceTask.cancel(); + enforceTask = null; + } + } + + + + /** + * 检查玩家是否豁免于游戏模式强制 + * 豁免条件:全局白名单 / 区域白名单 / 模式排除名单 + */ + private boolean isExemptFromGameMode( + Player p, String areaName) { + String name = p.getName(); + + /* if (p.isOp()) { + plugin.getLogger().info( + "[防护-调试] 豁免原因: OP, 玩家=" + name); + return true; + }*/ + if (globalPlayerWhitelist.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 全局白名单, 玩家=" + + name);*/ + return true; + } + Set aw = + areaPlayerWhitelist.get(areaName); + if (aw != null && aw.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 区域白名单, 玩家=" + + name + " 区域=" + areaName);*/ + return true; + } + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) { + /* plugin.getLogger().info( + "[防护-调试] 豁免原因: 模式排除名单, 玩家=" + + name);*/ + return true; + } + /* plugin.getLogger().info( + "[防护-调试] 无豁免, 玩家=" + name);*/ + return false; + } + + /** + * 玩家进入区域时,如果该区域强制游戏模式, + * 则立即设置(排除豁免玩家) + */ + private void applyEnforcedGameMode(Player p, + String areaName) { + AreaConfig ac = areas.get(areaName); + if (ac == null) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: ac=null, area=" + + areaName);*/ + return; + } + if (ac.enforceGameMode == null + || ac.enforceGameMode.isEmpty()) { + /* plugin.getLogger().info( + "[防护-调试] applyMode: 未配置强制模式, area=" + + areaName);*/ + return; + } + + boolean exempt = isExemptFromGameMode( + p, areaName); + /* plugin.getLogger().info( + "[防护-调试] applyMode: 玩家=" + p.getName() + + " 区域=" + areaName + + " 强制=" + ac.enforceGameMode + + " 当前=" + p.getGameMode() + + " 豁免=" + exempt + + " OP=" + p.isOp() + + " 全白=" + globalPlayerWhitelist + .contains(p.getName()));*/ + + if (exempt) return; + + try { + GameMode target = GameMode.valueOf( + ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + /* plugin.getLogger().info( + "[防护] 强制切换 " + p.getName() + + " → " + ac.enforceGameMode);*/ + } + } catch (Exception e) { + /* plugin.getLogger().warning( + "[防护] 强制模式失败: " + + e.getMessage());*/ + } + } + public AreaProtection(Main plugin) { this.plugin = plugin; - this.areaDir = new File(plugin.getDataFolder(), "区域防护"); - if (!areaDir.exists()) areaDir.mkdirs(); + + // 统一根目录 + rootDir = new File(plugin.getDataFolder(), "区域防护"); + if (!rootDir.exists()) rootDir.mkdirs(); + + // 白名单子目录 + whitelistDir = new File(rootDir, "whitelists"); + if (!whitelistDir.exists()) whitelistDir.mkdirs(); + globalWhitelistFile = new File( + whitelistDir, "全局白名单.txt"); + + // 数据库迁移 + oldDbFile = new File( + plugin.getDataFolder(), "area_effects.db"); + newDbFile = new File(rootDir, "area_effects.db"); + migrateDatabase(); + + // 初始化 initDatabase(); writeDefaultConfig(); loadAllAreas(); loadWhitelists(); + recoverPendingEffects(); } + public void recoverPendingEffects() { + try { + // 1. 遍历所有在线玩家(而不是遍历DB记录) + for (Player p : Bukkit.getOnlinePlayers()) { + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 2. 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + // 3. 获取该玩家在DB中的所有历史记录 + List recordedAreas = + getPlayerAreas(uid); + + // 4. 清理不在当前区域的旧记录 + for (String recordedArea : recordedAreas) { + if (currentAc == null + || !currentAc.name + .equals(recordedArea)) { + // 玩家不在这个区域了 → 清DB + removePlayerEffects( + uid, recordedArea); + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在" + recordedArea + + ", 清理旧记录"); + } + } + + // 5. 如果在区域中 → 恢复/给予效果 + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } else { + plugin.getLogger().info( + "[防护] 重连: " + playerName + + " 不在任何区域, 跳过"); + } + } + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 恢复效果失败: " + + e.getMessage()); + } + } + + + @EventHandler + public void onQuit(PlayerQuitEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + + // 取消延时清理任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 清标记 + playerMarkedEffects.remove(uid); + playerCurrentArea.remove(uid); + } + + /** + * 玩家上线时恢复区域效果 + * 如果玩家在有效果的区域中,恢复药水效果 + */ + public void onPlayerJoin(Player p) { + if (p == null || !p.isOnline()) return; + UUID uid = p.getUniqueId(); + String playerName = p.getName(); + + // 获取玩家当前所在区域 + AreaConfig currentAc = getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + + if (currentAc != null) { + plugin.getLogger().info( + "[防护] 上线: " + playerName + + " 在" + currentAc.name + + ", 恢复效果"); + applyRegionEffects(p, uid, + currentAc.name, currentAc); + } + } + + +// ==================== 数据库迁移 ==================== + + /** + * 如果旧位置有 db 文件,移到新位置 + * 如果新位置已有,不动 + * 如果旧位置也没有,在新位置创建 + */ + + private void migrateDatabase() { + if (newDbFile.exists()) { + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + oldDbFile.delete(); + } + return; + } + if (oldDbFile.exists() + && !oldDbFile.equals(newDbFile)) { + try { + java.nio.file.Files.move( + oldDbFile.toPath(), + newDbFile.toPath(), + java.nio.file.StandardCopyOption + .REPLACE_EXISTING); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 迁移数据库失败: " + + e.getMessage()); + } + } + } + // 保存玩家进入区域时给予的效果 private void savePlayerEffects(UUID uid, String playerName, @@ -238,7 +638,7 @@ public class AreaProtection implements Listener { private void writeDefaultConfig() { - File f = new File(areaDir, "末地保护区.txt"); + File f = new File(rootDir, "末地保护区.txt"); if (f.exists()) return; String endWorld = "world_the_end"; @@ -282,29 +682,128 @@ public class AreaProtection implements Listener { public void reload() { loadAllAreas(); loadWhitelists(); - recoverPendingEffects(); - plugin.getLogger().info("[防护] 已重载 " - + areas.size() + " 个区域"); + // 强制输出醒目的中文日志 + plugin.getLogger().info("========================================"); + plugin.getLogger().info("[防护] 已重载 " + areas.size() + " 个区域"); + plugin.getLogger().info("[防护] 已重载白名单"); + plugin.getLogger().info("========================================"); } - // ==================== 加载 ==================== +// ==================== 消息格式化 ==================== + + /** + * 格式化区域消息 + * 1. & → §(颜色符号转换) + * 2.
    和 \n → 换行 + * 3. 内容 → §l内容§r(加粗) + * 4. 自动添加 [区域防护] 前缀 + */ + private String formatAreaMsg(String raw) { + if (raw == null || raw.isEmpty()) return ""; + + String msg = raw; + + // & → § + for (int i = 0; i <= 9; i++) { + msg = msg.replace("&" + i, "§" + i); + } + for (char c : "abcdefklnor".toCharArray()) { + msg = msg.replace("&" + c, "§" + c); + } + + // 换行 + msg = msg.replace("
    ", "\n"); + msg = msg.replace("\\n", "\n"); + + // HTML → Minecraft格式(栈式解析,支持嵌套) + msg = convertHtmlTags(msg); + + // 前缀 + msg = "§c§l【区域防护】§r " + msg; + + return msg; + } + + /** + * 栈式HTML标签解析 + * 处理 text rest 这样的嵌套标签 + * 内层关闭时重新应用外层格式 + */ + private String convertHtmlTags(String msg) { + StringBuilder sb = new StringBuilder(); + java.util.Deque stack = + new ArrayDeque<>(); + int i = 0; + + while (i < msg.length()) { + if (msg.charAt(i) == '<' + && i + 1 < msg.length()) { + if (msg.charAt(i + 1) == '/') { + // 关闭标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 2, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0 && !stack.isEmpty() + && stack.peek() == code) { + stack.pop(); + if (!stack.isEmpty()) { + // 重置后重新应用外层格式 + sb.append("§r"); + for (char c : stack) { + sb.append("§").append(c); + } + } else { + sb.append("§r"); + } + } + i = end + 1; + } else { + // 开放标签 + int end = msg.indexOf('>', i); + if (end < 0) { + sb.append(msg.charAt(i)); + i++; + continue; + } + String tag = msg.substring(i + 1, end) + .trim().toLowerCase(); + char code = htmlToMc(tag); + if (code != 0) { + stack.push(code); + sb.append("§").append(code); + } + i = end + 1; + } + } else { + sb.append(msg.charAt(i)); + i++; + } + } + return sb.toString(); + } + + private char htmlToMc(String tag) { + switch (tag) { + case "b": return 'l'; + case "i": return 'o'; + case "u": return 'n'; + case "s": return 'm'; + default: return 0; + } + } + +// =============== 加载 ==================== public void loadAllAreas() { - // 验证所有区域的和平白名单 - plugin.getLogger().info("====== 区域解析验证 ======"); - for (Map.Entry entry : areas.entrySet()) { - AreaConfig ac = entry.getValue(); - plugin.getLogger().info("[验证] " + entry.getKey() - + " peaceMode=" + ac.peaceMode - + " peaceWhitelist=" + ac.peaceWhitelist - + " enforceGameMode=" + ac.enforceGameMode - + " modeExempt=" + ac.modeExempt); - } - plugin.getLogger().info("====== 验证结束 ======"); - areas.clear(); - File[] files = areaDir.listFiles( - (d, n) -> n.endsWith(".txt")); + File[] files = rootDir.listFiles( + (File d, String n) -> n.endsWith(".txt")); if (files == null) return; for (File f : files) { try { @@ -314,20 +813,16 @@ public class AreaProtection implements Listener { areas.put(name, ac); } catch (Exception e) { plugin.getLogger().warning( - "[防护] 加载失败: " - + f.getName()); - + "[防护] 加载失败: " + f.getName()); } - } - plugin.getLogger().info( - "[防护] 共加载 " + areas.size() + " 个区域"); } + + private void initDatabase() { try { - File dbFile = new File(plugin.getDataFolder(), "area_effects.db"); dbConnection = DriverManager.getConnection( - "jdbc:sqlite:" + dbFile.getAbsolutePath()); + "jdbc:sqlite:" + newDbFile.getAbsolutePath()); Statement stmt = dbConnection.createStatement(); stmt.executeUpdate( "CREATE TABLE IF NOT EXISTS player_effects (" @@ -338,15 +833,71 @@ public class AreaProtection implements Listener { + "effect_type TEXT NOT NULL," + "effect_level INTEGER DEFAULT 0," + "effect_duration INTEGER DEFAULT 999," - + "enter_time INTEGER NOT NULL" - + ")"); + + "enter_time INTEGER NOT NULL)"); stmt.executeUpdate( "CREATE INDEX IF NOT EXISTS idx_uuid " + "ON player_effects(uuid)"); stmt.close(); - plugin.getLogger().info("[防护] SQLite数据库初始化完成"); } catch (SQLException e) { - plugin.getLogger().severe("[防护] 数据库初始化失败: " + e.getMessage()); + plugin.getLogger().severe( + "[防护] 数据库初始化失败: " + + e.getMessage()); + } + } + + +// ==================== 效果名称中英文映射 ==================== + + /** + * 将中文效果名转为 Bukkit 能识别的英文名 + */ + private String resolveEffectName(String name) { + if (name == null) return null; + String n = name.trim(); + // 如果已经是英文或命名空间格式,直接用 + if (n.contains(":") + || n.equals(n.toUpperCase())) { + return n; + } + // 中文 → 英文映射 + switch (n) { + case "伤害吸收": return "ABSORPTION"; + case "生命提升": return "HEALTH_BOOST"; + case "抗性提升": return "DAMAGE_RESISTANCE"; + case "抗火": return "FIRE_RESISTANCE"; + case "夜视": return "NIGHT_VISION"; + case "力量": return "INCREASE_DAMAGE"; + case "速度": return "SPEED"; + case "急迫": return "FAST_DIGGING"; + case "跳跃提升": return "JUMP"; + case "再生": return "REGENERATION"; + case "生命恢复": return "REGENERATION"; + case "治疗": return "HEAL"; + case "瞬间治疗": return "HEAL"; + case "伤害药水": return "HARM"; + case "瞬间伤害": return "HARM"; + case "虚弱": return "WEAKNESS"; + case "中毒": return "POISON"; + case "缓慢": return "SLOW"; + case "挖掘疲劳": return "SLOW_DIGGING"; + case "失明": return "BLINDNESS"; + case "隐身": return "INVISIBILITY"; + case "发光": return "GLOWING"; + // 村庄英雄 + case "村庄英雄": return "HERO_OF_THE_VILLAGE"; + case "幸运": return "LUCK"; + case "不幸": return "UNLUCK"; + case "水下呼吸": return "WATER_BREATHING"; + case "水下速掘": return "DOLPHINS_GRACE"; + case "缓降": return "LEVITATION"; + case "漂浮": return "LEVITATION"; + case "饱和": return "SATURATION"; + // 无效效果 + case "伤害": return "HARM"; + case "治疗药水": return "HEAL"; + default: + // 尝试直接用英文匹配 + return n; } } @@ -427,9 +978,9 @@ public class AreaProtection implements Listener { for (int i = 0; i < line.length(); i++) { hex.append(String.format("U+%04X ", (int) line.charAt(i))); } - plugin.getLogger().info("[暴力调试] 行=[" + line + /* plugin.getLogger().info("[暴力调试] 行=[" + line + "] 长度=" + line.length() - + " 字符码=" + hex.toString()); + + " 字符码=" + hex.toString());*/ } // ===== 兜底扫描:确保和平白名单一定能解析 ===== if (line.indexOf("白名单") >= 0 @@ -606,14 +1157,55 @@ public class AreaProtection implements Listener { ac.peaceMode = true; plugin.getLogger().info("[解析] 和平模式已识别"); - } else if (line.startsWith("效果")) { + } else if (line.startsWith("效果")) { String rest = line.substring(2).trim(); + // 去掉前导冒号、空格 while (rest.length() > 0 && (rest.charAt(0) == ':' || rest.charAt(0) == ':' || rest.charAt(0) == ' ' || rest.charAt(0) == '\t')) { rest = rest.substring(1); + } + if (!rest.isEmpty()) { + String[] parts = rest.split("\\s+"); + // 第一个是效果名(可能是中文) + String effName = parts[0]; + int effLv = 1; + int effDur = 999; + if (parts.length >= 2) { + try { + effLv = Integer.parseInt(parts[1]); + } catch (NumberFormatException ignored) {} + } + if (parts.length >= 3) { + try { + effDur = Integer.parseInt(parts[2]); + } catch (NumberFormatException ignored) {} + } + + // ★ 用映射方法转为英文名 + String resolved = resolveEffectName(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → " + resolved + + " 等级=" + effLv + + " 时长=" + effDur); + + ac.giveEffects.add(new String[]{ + resolved, + String.valueOf(effLv), + String.valueOf(effDur)}); + // ★ 加日志确认解析结果 + PotionEffectType checkType = + resolveEffectType(effName); + plugin.getLogger().info("[防护] 解析效果: " + + effName + " → type=" + + (checkType != null + ? checkType.getName() + : "NULL(失败)") + + " 等级=" + effLv + + " 时长=" + effDur); + } if (!rest.isEmpty()) { @@ -645,12 +1237,33 @@ public class AreaProtection implements Listener { ac.confiscateMsg = line.substring( "没收提示:".length()).trim(); } else if (line.startsWith("进入提示:")) { - ac.enterMsg = line.substring( - "进入提示:".length()).trim(); + String raw = line.substring("进入提示:".length()).trim(); + ac.enterMsg = raw; + + // ★ 调试:打印原文和Unicode + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★进入提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); } else if (line.startsWith("离开提示:")) { - ac.leaveMsg = line.substring( - "离开提示:".length()).trim(); - } else if (line.startsWith("惩罚命令:")) { + String raw = line.substring("离开提示:".length()).trim(); + ac.leaveMsg = raw; + + StringBuilder hex = new StringBuilder(); + for (int i = 0; i < raw.length(); i++) { + hex.append(String.format("U+%04X ", + (int) raw.charAt(i))); + } + plugin.getLogger().info( + "[防护-解析] ★★★离开提示原文=[" + raw + + "] 长度=" + raw.length() + + " Unicode=" + hex.toString()); + } else if (line.startsWith("惩罚命令:")) { ac.punishCommands.add(line.substring( "惩罚命令:".length()).trim()); } else if (line.startsWith("强制游戏模式:") @@ -938,127 +1551,58 @@ public class AreaProtection implements Listener { return HOSTILE_TYPES.contains(typeName); } - // ==================== 白/黑名单持久化 ==================== + // ==================== 白名单持久化 ==================== - private File getWhitelistFile(String type, String area) { - String fileName = type + "_" + area + ".txt"; - return new File(areaDir, "_whitelists/" + fileName); - } + /** + * 保存白名单到文件 + * 路径:区域防护/whitelists/全局白名单.txt + * 路径:区域防护/whitelists/区域名.txt + */ + public void saveWhitelists() { + try { + // 1. 保存全局白名单 + PrintWriter pwGlobal = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(globalWhitelistFile), + StandardCharsets.UTF_8)); + for (String name : globalPlayerWhitelist) { + pwGlobal.println(name); + } + pwGlobal.close(); - private void loadWhitelists() { - File wlDir = new File(areaDir, "_whitelists"); - if (!wlDir.exists()) wlDir.mkdirs(); + // 2. 删除旧的区域白名单文件(防止残留) + File[] oldFiles = whitelistDir.listFiles( + (d, n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (oldFiles != null) { + for (File f : oldFiles) f.delete(); + } - // 读全局玩家白名单 - File f = new File(wlDir, "global_player.txt"); - if (f.exists()) { - try { - BufferedReader br = new BufferedReader( - new InputStreamReader( - new FileInputStream(f), + // 3. 保存各区域白名单 + for (Map.Entry> entry + : areaPlayerWhitelist.entrySet()) { + String name = entry.getKey(); + File areaFile = new File( + whitelistDir, name + ".txt"); + PrintWriter pwArea = new PrintWriter( + new OutputStreamWriter( + new FileOutputStream(areaFile), StandardCharsets.UTF_8)); - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) - globalPlayerWhitelist.add(line.toLowerCase()); + for (String playerName : entry.getValue()) { + pwArea.println(playerName); } - br.close(); - } catch (IOException ignored) { + pwArea.close(); } - } - // 读全局物品黑名单 - f = new File(wlDir, "global_item.txt"); - if (f.exists()) { - try { - BufferedReader br = new BufferedReader( - new InputStreamReader( - new FileInputStream(f), - StandardCharsets.UTF_8)); - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) - globalItemBlacklist.add(line.toUpperCase()); - } - br.close(); - } catch (IOException ignored) { - } - } - - // 读区域白名单 - for (String area : areas.keySet()) { - loadAreaWhitelist(area, "player", areaPlayerWhitelist); - loadAreaWhitelist(area, "item", areaItemBlacklist); + plugin.getLogger().info("[防护] 白名单已保存"); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 保存白名单失败: " + + e.getMessage()); } } - private void loadAreaWhitelist(String area, String type, - Map> map) { - File wlDir = new File(areaDir, "_whitelists"); - File f = new File(wlDir, area + "_" + type + ".txt"); - if (!f.exists()) return; - try { - Set set = map.computeIfAbsent( - area, k -> ConcurrentHashMap.newKeySet()); - BufferedReader br = new BufferedReader( - new InputStreamReader( - new FileInputStream(f), - StandardCharsets.UTF_8)); - String line; - while ((line = br.readLine()) != null) { - line = line.trim(); - if (!line.isEmpty()) set.add(line.toLowerCase()); - } - br.close(); - } catch (IOException ignored) { - } - } - private void saveGlobalWhitelist() { - File wlDir = new File(areaDir, "_whitelists"); - if (!wlDir.exists()) wlDir.mkdirs(); - try { - PrintWriter pw = new PrintWriter( - new OutputStreamWriter( - new FileOutputStream( - new File(wlDir, "global_player.txt")), - StandardCharsets.UTF_8)); - for (String s : globalPlayerWhitelist) - pw.println(s); - pw.close(); - } catch (IOException ignored) { - } - try { - PrintWriter pw = new PrintWriter( - new OutputStreamWriter( - new FileOutputStream( - new File(wlDir, "global_item.txt")), - StandardCharsets.UTF_8)); - for (String s : globalItemBlacklist) - pw.println(s); - pw.close(); - } catch (IOException ignored) { - } - } - - private void saveAreaWhitelist(String area, String type, - Set set) { - File wlDir = new File(areaDir, "_whitelists"); - if (!wlDir.exists()) wlDir.mkdirs(); - try { - PrintWriter pw = new PrintWriter( - new OutputStreamWriter( - new FileOutputStream( - new File(wlDir, - area + "_" + type + ".txt")), - StandardCharsets.UTF_8)); - for (String s : set) pw.println(s); - pw.close(); - } catch (IOException ignored) { - } - } // ==================== 区域检测 ==================== @@ -1179,7 +1723,7 @@ public class AreaProtection implements Listener { playerCurrentArea.clear(); } private void saveModeExempt(AreaConfig ac) { - File file = new File(areaDir, ac.name + ".txt"); + File file = new File(rootDir, ac.name + ".txt"); if (!file.exists()) return; try { @@ -1235,57 +1779,562 @@ public class AreaProtection implements Listener { // ==================== 事件监听 ==================== @EventHandler - public void onPlayerMove(PlayerMoveEvent e) { - Player p = e.getPlayer(); - Location from = e.getFrom(); - Location to = e.getTo(); + public void onPlayerMove(PlayerMoveEvent event) { + Player p = event.getPlayer(); + UUID uid = p.getUniqueId(); + Location to = event.getTo(); if (to == null) return; - // 获取当前位置的区域 - AreaConfig toArea = getArea( - to.getWorld().getName(), - to.getBlockX(), to.getBlockY(), - to.getBlockZ()); + Location from = event.getFrom(); + if (from.getBlockX() == to.getBlockX() + && from.getBlockZ() == to.getBlockZ()) { + return; + } - // 获取上一tick位置的区域 - AreaConfig fromArea = getArea( - from.getWorld().getName(), - from.getBlockX(), from.getBlockY(), - from.getBlockZ()); + // 防抖 + Long lastTime = lastMoveProcess.get(uid); + long now = System.currentTimeMillis(); + if (lastTime != null + && now - lastTime < MOVE_DEBOUNCE_MS) { + return; + } + lastMoveProcess.put(uid, now); - // 用 playerCurrentArea 追踪区域变化 - String toAreaName = (toArea != null) ? toArea.name : null; - String lastAreaName = playerCurrentArea.get(p.getName()); + // 检测区域 + String oldArea = playerCurrentArea.get(uid); + String newArea = null; + String forcedMode = null; - if (toAreaName != null) { - if (!toAreaName.equals(lastAreaName)) { - // 进入了新区域 - // 先离开旧区域 - if (lastAreaName != null) { - AreaConfig oldAc = areas.get(lastAreaName); - if (oldAc != null) { - handleLeave(p, oldAc); + for (Map.Entry entry + : areas.entrySet()) { + AreaConfig ac = entry.getValue(); + if (!ac.world.equals(to.getWorld().getName())) + continue; + int px = to.getBlockX(); + int pz = to.getBlockZ(); + int py = to.getBlockY(); + int minX = Math.min(ac.x1, ac.x2); + int maxX = Math.max(ac.x1, ac.x2); + int minZ = Math.min(ac.z1, ac.z2); + int maxZ = Math.max(ac.z1, ac.z2); + if (px >= minX && px <= maxX + && pz >= minZ && pz <= maxZ + && py >= ac.yMin && py <= ac.yMax) { + newArea = entry.getKey(); + forcedMode = ac.enforceGameMode; + break; + } + } + + /* plugin.getLogger().info( + "[防护-移动] " + p.getName() + + " 旧=[" + oldArea + + "] 新=[" + newArea + "]" + + " areas.size=" + areas.size() + + " world=" + to.getWorld().getName());*/ + + // ===== 2. 区域没变 ===== + if (Objects.equals(oldArea, newArea)) { + if (newArea != null) { + AreaConfig ac = areas.get(newArea); + if (ac != null) { + // ★ 每次移动都清除指定效果 + clearBadEffects(p, ac); + + // ★ 每次移动都强制游戏模式 + if (ac.enforceGameMode != null + && !ac.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(ac.enforceGameMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } } } - // 进入新区域 - handleEnter(p, toArea); - playerCurrentArea.put(p.getName(), toAreaName); } - // 在同一区域内 → 调用 handleInside(持续检查) - handleInside(p, toArea); + return; + } + + // 离开旧区域 + if (oldArea != null) { + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★开始离开处理: + + oldArea);*/ + + AreaConfig oldAc = areas.get(oldArea); + + // 离开提示 + if (oldAc != null && oldAc.leaveMsg != null + && !oldAc.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(oldAc.leaveMsg)); + } + + // 收标 + List marked = + playerMarkedEffects.remove(uid); + + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★收标: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + plugin.getLogger().info( + "[防护-移动-离开] 标记项: " + t.getName()); + } + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + plugin.getLogger().info( + "[防护-移动-离开] 取消旧延时"); + } + + // 创建延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = oldArea; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + /* plugin.getLogger().info( + "[防护-移动-离开] ★★★创建延时任务: " + + toClear.size() + "个");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-移动-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); + + plugin.getLogger().info( + "[防护-移动-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size()); + + if (online == null || !online.isOnline()) { + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-移动-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-移动-清理] + cleared" + toClear.size()); + } + }.runTaskLater(plugin, 100L); // 5秒 = 100 tick + pendingClearTask.put(uid, task); + } + } + + + // 进入新区域 + if (newArea != null) { + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + if (oldP != null) { + diffEffects(p, uid, oldP, newArea); + } + } + + AreaConfig newAc = areas.get(newArea); + if (newAc != null) { + if (newAc.enterMsg != null + && !newAc.enterMsg.isEmpty()) { + p.sendMessage( + formatAreaMsg(newAc.enterMsg)); + } + applyRegionEffects(p, uid, newArea, newAc); + // 清除效果 + clearBadEffects(p, newAc); + + } +// ===== 清除指定效果 ===== + if (newAc.clearEffects != null + && !newAc.clearEffects.isEmpty()) { + for (String effName : newAc.clearEffects) { + PotionEffectType type = + resolveEffectType(effName); + if (type != null && p.getPotionEffect(type) != null) { + p.removePotionEffect(type); + plugin.getLogger().info( + "[防护] ★清除效果: " + effName + + " 玩家=" + p.getName()); + } + } + } + +// ===== 清除负面效果(clearBadEffects 已在上方处理) ===== + // clearAllBadEffects 仅清除负面+中性效果,不清除正面效果 + // 此处不再单独处理,统一由上方 clearBadEffects() 负责 + + if (forcedMode != null + && !isExemptFromGameMode(p, newArea)) { + GameMode target = + GameMode.valueOf(forcedMode); + if (p.getGameMode() != target) { + p.setGameMode(target); + } + } + } + + if (newArea != null) { + playerCurrentArea.put(uid, newArea); } else { - // 不在任何区域内 - if (lastAreaName != null) { - // 离开了区域 - AreaConfig oldAc = areas.get(lastAreaName); - if (oldAc != null) { - handleLeave(p, oldAc); + playerCurrentArea.remove(uid); + } + } +// ===== 清理袭击生物 ===== + + // 需要清理的袭击生物类型 + private static final Set RAID_MOBS = new HashSet<>(Arrays.asList( + "VEX", + "EVOKER", + "PILLAGER", + "VINDICATOR", + "WITCH", + "RAVAGER", + "PILLAGER" + )); + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + Entity entity = event.getEntity(); + Location loc = entity.getLocation(); + + // 检查是否在防护区域内 + AreaConfig ac = getArea( + loc.getWorld().getName(), + loc.getBlockX(), loc.getBlockY(), + loc.getBlockZ()); + if (ac == null) return; + + // 禁止袭击:取消袭击事件 + if (ac.denyRaid) { + // 清理袭击生物 + String typeName = entity.getType().name(); + if (RAID_MOBS.contains(typeName)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] ★清理袭击生物: " + typeName + + " 区域=" + ac.name); + } + } + + // 没收物品检测 + if (ac.confiscateItems != null + && !ac.confiscateItems.isEmpty()) { + if (entity instanceof Item) { + Item item = (Item) entity; + String itemType = item.getItemStack() + .getType().name(); + if (ac.confiscateItems.contains(itemType)) { + event.setCancelled(true); + plugin.getLogger().info( + "[防护] 没收违禁掉落物: " + + itemType); } - playerCurrentArea.remove(p.getName()); } } } + + /** + * 进入区域时发放效果 + * 流程:查DB → 有记录跳过写入 → 无记录写入DB + * → 给效果 → 贴标 + */ + private void applyRegionEffects(Player p, UUID uid, + String areaName, AreaConfig ac) { + /* plugin.getLogger().info( + "[防护-贴标] ★★★方法被调用! 玩家=" + + p.getName() + " 区域=" + areaName + + " ac=" + (ac != null) + + " 效果配置数=" + + (ac != null ? ac.giveEffects.size() : "null"));*/ + + if (ac == null || ac.giveEffects.isEmpty()) return; + if (ac.giveEffects.isEmpty()) return; + // 查DB:该玩家在此区域是否已有效果记录 + List dbEffects = + getPlayerEffectNames(uid, areaName); + + if (dbEffects.isEmpty()) { + // 无记录 → 写入DB + for (String[] eff : ac.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t == null) continue; + saveSingleEffect(uid, p.getName(), + areaName, t.getName()); + } + plugin.getLogger().info( + "[防护] DB写入, 玩家=" + p.getName() + + " 区域=" + areaName); + } else { + plugin.getLogger().info( + "[防护] DB已有记录, 跳过写入, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + + // 给效果 + 贴标 + List applied = new ArrayList<>(); + for (String[] eff : ac.giveEffects) { + try { + PotionEffectType type = + resolveEffectType(eff[0]); + if (type == null) continue; + int lv = Integer.parseInt(eff[1]) - 1; + int dur = Integer.parseInt(eff[2]) * 20; + + PotionEffect existing = + p.getPotionEffect(type); + if (existing != null + && existing.getAmplifier() >= lv) { + continue; + } + p.addPotionEffect( + new PotionEffect(type, dur, lv)); + applied.add(type); + } catch (Exception ignored) { + } + } + + // 贴标 + // 新代码(合并,不覆盖): + List existingMarks = + playerMarkedEffects.get(uid); + if (existingMarks != null && !existingMarks.isEmpty()) { + // 已有标记,合并新增的 + for (PotionEffectType t : applied) { + if (!existingMarks.contains(t)) { + existingMarks.add(t); + } + } + /* plugin.getLogger().info( + "[防护-贴标] 合并标记: " + + existingMarks.size() + + " 个, 玩家=" + p.getName());*/ + } else { + // 无标记,新建 + playerMarkedEffects.put(uid, applied); + /* plugin.getLogger().info( + "[防护-贴标] 新建标记: " + + applied.size() + + " 个, 玩家=" + p.getName());*/ + } + } + + /** + * 离开区域时,延时5秒清理效果 + * 流程:收标 → 5秒后清效果 → 确认清空 → 清DB + */ + private void scheduleEffectClear(Player p, UUID uid, + String areaName) { + // 收标 + List marked = + playerAppliedEffects.remove(uid); + + plugin.getLogger().info( + "[防护] 收标 " + (marked != null ? marked.size() : 0) + + " 个, 玩家=" + p.getName() + + " 区域=" + areaName); + + // 启动5秒延时 + pendingClearArea.put(uid, areaName); + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + + // 清除效果 + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + + // 确认清空后清DB + removePlayerEffects(uid, areaName); + plugin.getLogger().info( + "[防护] 5秒已到, 清除 " + + (marked != null ? marked.size() : 0) + + " 个效果 + DB, 玩家=" + + p.getName() + + " 区域=" + areaName); + } + }.runTaskLater(plugin, 100L); // 5秒 = 100 tick + + pendingClearTask.put(uid, task); + } + public Set getAllAreaNames() { + return areas.keySet(); + } + + /** + * 5秒内进了新区域: + * 清除旧区域有、新区域没有的效果 + */ + private void diffEffects(Player p, UUID uid, + String oldAreaName, String newAreaName) { + AreaConfig oldAc = areas.get(oldAreaName); + AreaConfig newAc = areas.get(newAreaName); + if (oldAc == null || newAc == null) return; + + // ★ 从 pendingClearEffects 获取旧标记 + List oldMarked = + pendingClearEffects.remove(uid); + + if (oldMarked == null || oldMarked.isEmpty()) { + plugin.getLogger().info( + "[防护-跨区域] 无待清理效果"); + return; + } + + // 新区域的效果集合 + Set newTypes = new HashSet<>(); + if (newAc != null) { + for (String[] eff : newAc.giveEffects) { + PotionEffectType t = + resolveEffectType(eff[0]); + if (t != null) newTypes.add(t); + } + } + + int cleared = 0; + for (PotionEffectType type : oldMarked) { + if (!newTypes.contains(type)) { + p.removePotionEffect(type); + cleared++; + plugin.getLogger().info( + "[防护-跨区域] 清除: " + + type.getName()); + } else { + plugin.getLogger().info( + "[防护-跨区域] 保留: " + + type.getName() + + " (新区域也有)"); + } + } + + removePlayerEffects(uid, oldAreaName); + plugin.getLogger().info( + "[防护-跨区域] 完成: 清除" + + cleared + "/" + oldMarked.size() + + " 个, " + oldAreaName + + " → " + newAreaName); + } + +// ===== DB 辅助方法(简化版)===== + + /** + * 查询玩家在某个区域的效果记录(返回英文名列表) + */ + private List getPlayerEffectNames( + UUID uid, String areaName) { + List list = new ArrayList<>(); + try { + PreparedStatement ps = dbConnection.prepareStatement( + "SELECT effect_type FROM player_effects " + + "WHERE uuid=? AND area_name=?"); + ps.setString(1, uid.toString()); + ps.setString(2, areaName); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + list.add(rs.getString("effect_type")); + } + rs.close(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 查询效果失败: " + e.getMessage()); + } + return list; + } + + /** + * 写入单条效果记录 + */ + private void saveSingleEffect(UUID uid, + String playerName, String areaName, + String effectType) { + try { + PreparedStatement ps = dbConnection.prepareStatement( + "INSERT INTO player_effects " + + "(uuid, player_name, area_name, " + + "effect_type, effect_level, " + + "effect_duration, enter_time) " + + "VALUES (?,?,?,?,?,?,?)"); + ps.setString(1, uid.toString()); + ps.setString(2, playerName); + ps.setString(3, areaName); + ps.setString(4, effectType); + ps.setInt(5, 0); + ps.setInt(6, 999); + ps.setLong(7, System.currentTimeMillis()); + ps.executeUpdate(); + ps.close(); + } catch (SQLException e) { + plugin.getLogger().warning( + "[防护] 写入效果失败: " + e.getMessage()); + } + } + + // ==================== 违禁品没收 ==================== + + /** + * 检查玩家是否豁免于没收 + * 全局白名单 / 区域白名单 / modeExempt / OP 都跳过 + */ + public boolean isExemptFromConfiscation( + Player p, String areaName) { + String name = p.getName(); + + if (globalPlayerWhitelist.contains(name)) + return true; + + Set areaWl = + areaPlayerWhitelist.get(areaName); + if (areaWl != null && areaWl.contains(name)) + return true; + + AreaConfig ac = areas.get(areaName); + if (ac != null && ac.modeExempt.contains(name)) + return true; + + return false; + } + + private String getChineseGameMode(GameMode mode) { switch (mode) { case SURVIVAL: return "生存模式"; @@ -1299,13 +2348,6 @@ public class AreaProtection implements Listener { private void handleEnter(Player p, AreaConfig ac) { UUID uid = p.getUniqueId(); - // 进入提示 - if (ac.enterMsg != null && !ac.enterMsg.isEmpty()) { - p.sendTitle("§c§l" + ac.name, - "§e" + ac.enterMsg, 10, 60, 20); - p.sendMessage("§c§l[区域防护] §f" + ac.enterMsg); - } - // 清除负面效果 clearBadEffects(p, ac); @@ -1329,7 +2371,7 @@ public class AreaProtection implements Listener { removePlayerEffects(uid, ac.name); if (!given.isEmpty()) { - savePlayerEffects(uid, p.getName(), ac.name, given); + // savePlayerEffects(uid, p.getName(), ac.name, given); } // 白名单检查(最先执行) @@ -1452,95 +2494,96 @@ public class AreaProtection implements Listener { switch (clean) { // 负面 case "缓慢": - return PotionEffectType.getByName("slowness"); + return resolveEffectType("slowness"); case "挖掘疲劳": - return PotionEffectType.getByName("mining_fatigue"); + return resolveEffectType("mining_fatigue"); case "瞬间伤害": - return PotionEffectType.getByName("instant_damage"); + return resolveEffectType("instant_damage"); case "反胃": - return PotionEffectType.getByName("nausea"); + return resolveEffectType("nausea"); case "失明": - return PotionEffectType.getByName("blindness"); + return resolveEffectType("blindness"); case "饥饿": - return PotionEffectType.getByName("hunger"); + return resolveEffectType("hunger"); case "虚弱": - return PotionEffectType.getByName("weakness"); + return resolveEffectType("weakness"); case "中毒": - return PotionEffectType.getByName("poison"); + return resolveEffectType("poison"); case "凋零": - return PotionEffectType.getByName("wither"); + return resolveEffectType("wither"); case "飘浮": - return PotionEffectType.getByName("levitation"); + return resolveEffectType("levitation"); case "霉运": - return PotionEffectType.getByName("unluck"); + return resolveEffectType("unluck"); case "黑暗": - return PotionEffectType.getByName("darkness"); + return resolveEffectType("darkness"); case "蓄风": - return PotionEffectType.getByName("wind_charged"); + return resolveEffectType("wind_charged"); case "盘丝": - return PotionEffectType.getByName("weaving"); + return resolveEffectType("weaving"); case "渗浆": - return PotionEffectType.getByName("oozing"); + return resolveEffectType("oozing"); case "寄生": - return PotionEffectType.getByName("infested"); + return resolveEffectType("infested"); // 中性 case "不祥之兆": case "不祥征兆": - return PotionEffectType.getByName("bad_omen"); + return resolveEffectType("bad_omen"); case "袭击之兆": case "袭击征兆": - return PotionEffectType.getByName("raid_omen"); + return resolveEffectType("raid_omen"); case "试炼之兆": case "试炼征兆": - return PotionEffectType.getByName("trial_omen"); + return resolveEffectType("trial_omen"); // 正面 case "迅捷": - return PotionEffectType.getByName("speed"); + return resolveEffectType("speed"); case "急迫": - return PotionEffectType.getByName("haste"); + return resolveEffectType("haste"); case "力量": - return PotionEffectType.getByName("strength"); + return resolveEffectType("strength"); case "瞬间治疗": - return PotionEffectType.getByName("instant_health"); + return resolveEffectType("instant_health"); case "跳跃提升": - return PotionEffectType.getByName("jump_boost"); + return resolveEffectType("jump_boost"); case "生命恢复": - return PotionEffectType.getByName("regeneration"); + return resolveEffectType("regeneration"); case "抗性提升": - return PotionEffectType.getByName("resistance"); + return resolveEffectType("resistance"); case "抗火": - return PotionEffectType.getByName("fire_resistance"); + return resolveEffectType("fire_resistance"); case "水下呼吸": - return PotionEffectType.getByName("water_breathing"); + return resolveEffectType("water_breathing"); case "隐身": - return PotionEffectType.getByName("invisibility"); + return resolveEffectType("invisibility"); case "夜视": - return PotionEffectType.getByName("night_vision"); + return resolveEffectType("night_vision"); case "发光": - return PotionEffectType.getByName("glowing"); + return resolveEffectType("glowing"); case "生命提升": - return PotionEffectType.getByName("health_boost"); + return resolveEffectType("health_boost"); case "伤害吸收": - return PotionEffectType.getByName("absorption"); + return resolveEffectType("absorption"); case "饱和": - return PotionEffectType.getByName("saturation"); + return resolveEffectType("saturation"); case "幸运": - return PotionEffectType.getByName("luck"); + return resolveEffectType("luck"); case "村庄英雄": - return PotionEffectType.getByName("hero_of_the_village"); + return resolveEffectType("hero_of_the_village"); case "缓降": - return PotionEffectType.getByName("slow_falling"); + return resolveEffectType("slow_falling"); case "潮涌能量": - return PotionEffectType.getByName("conduit_power"); + return resolveEffectType("conduit_power"); case "海豚的恩惠": - return PotionEffectType.getByName("dolphins_grace"); + return resolveEffectType("dolphins_grace"); case "致命中毒": - return PotionEffectType.getByName("poison"); + return resolveEffectType("poison"); default: break; } - // 兜底:直接用原版英文ID查 + +// 兜底:直接用原版英文ID查(★ 注意是 PotionEffectType.getByName,不是 resolveEffectType) PotionEffectType fallback = PotionEffectType.getByName(clean); if (fallback != null) return fallback; fallback = PotionEffectType.getByName(clean.toLowerCase()); @@ -1548,6 +2591,7 @@ public class AreaProtection implements Listener { plugin.getLogger().warning("[防护] 无法识别效果: " + name); return null; + } @@ -1576,94 +2620,9 @@ public class AreaProtection implements Listener { } } - private void handleLeave(Player p, AreaConfig ac) { - UUID uid = p.getUniqueId(); - - if (ac.leaveMsg != null && !ac.leaveMsg.isEmpty()) { - p.sendTitle("§a§l离开区域", "§e" + ac.leaveMsg, 10, 40, 10); - p.sendMessage("§a§l[区域防护] §f" + ac.leaveMsg); - } - - List given = getPlayerEffects(uid, ac.name); - - plugin.getLogger().info("[防护] " + p.getName() - + " 离开" + ac.name - + " DB效果数=" + given.size()); - - if (!given.isEmpty()) { - lastLeaveTime.put(uid, System.currentTimeMillis()); - pendingClear.put(uid, new ArrayList<>(given)); - plugin.getLogger().info("[防护] 已记录待清除 lastLeaveTime=" - + lastLeaveTime.containsKey(uid) - + " pendingClear数量=" - + pendingClear.get(uid).size()); - } - - cancelBorder(p); - } - - public void recoverPendingEffects() { - List pending = getAllPendingEffects(); - if (pending.isEmpty()) return; - - plugin.getLogger().info("[防护] 恢复" - + pending.size() + "条未处理的效果记录"); - - for (String[] row : pending) { - String uuidStr = row[0]; - String playerName = row[1]; - String areaName = row[2]; - String effectName = row[3]; - - UUID uid = UUID.fromString(uuidStr); - Player p = Bukkit.getPlayer(uid); - - // 玩家不在线,直接清理DB - if (p == null || !p.isOnline()) { - plugin.getLogger().info("[防护] " - + playerName + "不在线,清理DB记录"); - removePlayerEffects(uid, areaName); - continue; - } - - // 玩家在线,检查当前是否还在该区域 - AreaConfig ac = areas.get(areaName); - if (ac == null) { - removePlayerEffects(uid, areaName); - continue; - } - - AreaConfig currentArea = getArea( - p.getWorld().getName(), - p.getLocation().getBlockX(), - p.getLocation().getBlockY(), - p.getLocation().getBlockZ()); - - if (currentArea == null - || !currentArea.name.equals(areaName)) { - // 不在原来区域了,清除效果 - PotionEffectType type = PotionEffectType - .getByName(effectName); - if (type != null) { - p.removePotionEffect(type); - plugin.getLogger().info("[防护] " - + playerName + " 重连后清除: " - + effectName); - } - removePlayerEffects(uid, areaName); - } else { - // 还在原来区域,重新给予效果 - PotionEffectType type = PotionEffectType - .getByName(effectName); - if (type != null) { - p.addPotionEffect(new PotionEffect( - type, 999 * 20, 0)); - plugin.getLogger().info("[防护] " - + playerName + " 重连后重新给予: " - + effectName); - } - } - } + public Map> + getPendingClearEffects() { + return pendingClearEffects; } public void onPlayerOffline(UUID uid, String playerName, @@ -1679,86 +2638,28 @@ public class AreaProtection implements Listener { } // 清除DB记录 removePlayerEffects(uid, areaName); - lastLeaveTime.remove(uid); - pendingClear.remove(uid); plugin.getLogger().info("[防护] " + playerName + " 下线,已清除" + given.size() + "个效果并清理DB"); } - - public void checkPendingClears() { - long now = System.currentTimeMillis(); - Iterator> it = - lastLeaveTime.entrySet().iterator(); - /*plugin.getLogger().info("[防护] checkPendingClears执行" - + " 待处理=" + lastLeaveTime.size());*/ - while (it.hasNext()) { - Map.Entry entry = it.next(); - UUID uid = entry.getKey(); - long leaveTime = entry.getValue(); - - if (now - leaveTime < 15000) continue; - - Player p = Bukkit.getPlayer(uid); - if (p == null || !p.isOnline()) { - it.remove(); - pendingClear.remove(uid); - continue; - } - - List toClear = pendingClear.get(uid); - if (toClear == null || toClear.isEmpty()) { - it.remove(); - continue; - } - - AreaConfig currentArea = getArea( - p.getWorld().getName(), - p.getLocation().getBlockX(), - p.getLocation().getBlockY(), - p.getLocation().getBlockZ()); - - if (currentArea == null) { - // 不在任何区域:只清除DB记录的效果,不动玩家其他效果 - for (PotionEffectType type : toClear) { - p.removePotionEffect(type); - } - removeAllPlayerEffects(uid); - plugin.getLogger().info("[防护] " + p.getName() - + " 全清效果: " + toClear); - } else { - // 在新区域:只清新区域没有的效果 - List newGiven = - getPlayerEffects(uid, currentArea.name); - if (newGiven == null) newGiven = new ArrayList<>(); - - for (PotionEffectType type : toClear) { - if (!newGiven.contains(type)) { - p.removePotionEffect(type); - } - } - // 清除旧区域DB记录 - removePlayerEffects(uid,""); - plugin.getLogger().info("[防护] " + p.getName() - + " 在新区域, 清除旧效果"); - } - - it.remove(); - pendingClear.remove(uid); - } - } - @EventHandler public void onTeleport(PlayerTeleportEvent e) { Location from = e.getFrom(); Location to = e.getTo(); if (to == null) return; - // 忽略同世界内短距离传送(如骑马、矿车) + if (from.getWorld() == to.getWorld() && from.distance(to) < 5) return; Player p = e.getPlayer(); + UUID uid = p.getUniqueId(); + + // 标记传送中 + teleporting.add(uid); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + teleporting.remove(uid); + }, 5L); AreaConfig fromArea = getArea( from.getWorld().getName(), @@ -1769,45 +2670,219 @@ public class AreaProtection implements Listener { to.getBlockX(), to.getBlockY(), to.getBlockZ()); - // 从区域内传送到区域外 - if (fromArea != null && toArea == null) { - handleLeave(p, fromArea); - } - // 从区域外传送到区域内 - else if (fromArea == null && toArea != null) { - // plugin.getLogger().info("[防护] 触发handleEnter"); - handleEnter(p, toArea); + String fromName = fromArea != null + ? fromArea.name : null; + String toName = toArea != null + ? toArea.name : null; + + /* plugin.getLogger().info( + "[防护-传送] ★★★进入onTeleport: " + + p.getName() + + " [" + fromName + "] → [" + toName + "]" + + " teleporting=" + teleporting.contains(uid));*/ + + if (Objects.equals(fromName, toName)) { + /* plugin.getLogger().info( + "[防护-传送] 区域相同, 跳过");*/ + return; } - // 从一个区域传送到另一个区域 - else if (fromArea != null && toArea != null - && !fromArea.name.equals(toArea.name)) { - handleLeave(p, fromArea); - handleEnter(p, toArea); - } - } + // ===== 离开旧区域 ===== + if (fromName != null) { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★开始离开处理: " + + fromName);*/ - public void handleInside(Player p, AreaConfig ac) { - clearBadEffects(p, ac); + // 收标 + List marked = + playerMarkedEffects.remove(uid); - // 和平模式 - if (ac.peaceMode) { - banHostilesWithWhitelist(p, ac); - } else if (ac.denyRaid) { - banRaidMobs(p, ac); + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★收标结果: " + + (marked != null ? marked.size() : "null"));*/ + + if (marked != null) { + for (PotionEffectType t : marked) { + /* plugin.getLogger().info( + "[防护-传送-离开] 标记项: " + + t.getName());*/ + } + } else { + /* plugin.getLogger().info( + "[防护-传送-离开] ★★标记为null! " + + "尝试其他key查找...");*/ + // 遍历查找可能的标记 + for (Map.Entry> entry + : playerMarkedEffects.entrySet()) { + if (entry.getKey().equals(uid)) { + /* plugin.getLogger().info( + "[防护-传送-离开] 找到标记! 效果数=" + + entry.getValue().size());*/ + } + } + /* plugin.getLogger().info( + "[防护-传送-离开] playerMarkedEffects大小=" + + playerMarkedEffects.size());*/ + } + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + } + + // 创建5秒延时清理 + if (marked != null && !marked.isEmpty()) { + String clearArea = fromName; + UUID clearUid = uid; + List toClear = + new ArrayList<>(marked); + pendingClearArea.put(uid, clearArea); + pendingClearEffects.put(uid, toClear); + + /* plugin.getLogger().info( + "[防护-传送-离开] ★★★创建延时任务: " + + toClear.size() + "个效果");*/ + + BukkitTask task = new BukkitRunnable() { + @Override + public void run() { + /* plugin.getLogger().info( + "[防护-传送-清理] ★★★延时触发!");*/ + pendingClearTask.remove(clearUid); + pendingClearArea.remove(clearUid); + + Player online = + Bukkit.getPlayer(clearUid); +/* + plugin.getLogger().info( + "[防护-传送-清理] 在线=" + + (online != null && online.isOnline()) + + " 效果数=" + toClear.size() + + " 区域=" + clearArea);*/ + + if (online == null + || !online.isOnline()) { + plugin.getLogger().info( + "[防护-传送-清理] 玩家不在线, 清DB"); + removePlayerEffects( + clearUid, clearArea); + return; + } + + int cleared = 0; + for (PotionEffectType type : toClear) { + PotionEffect eff = + online.getPotionEffect(type); + /* plugin.getLogger().info( + "[防护-传送-清理] 检查: " + + type.getName() + + " 身上=" + (eff != null));*/ + if (eff != null) { + online.removePotionEffect(type); + cleared++; + } + } + removePlayerEffects(clearUid, clearArea); + plugin.getLogger().info( + "[防护-传送-清理] ★★★完成: 清除" + + cleared + "/" + toClear.size()); + } + }.runTaskLater(plugin, 100L); // 5秒 = 100 tick + pendingClearTask.put(uid, task); + + } + + // 离开提示 + if (fromArea != null && fromArea.leaveMsg != null + && !fromArea.leaveMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(fromArea.leaveMsg)); + } } - // 游戏模式:每次都强制切换(排除全白/区白/豁免/OP) - if (ac.enforceGameMode != null) { - if (!isPlayerExemptFromModeChange(p, ac)) { - GameMode target = GameMode.valueOf(ac.enforceGameMode); + // ===== 进入新区域 ===== + if (toName != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] ★★★开始进入处理: " + + toName);*/ + + // 取消旧延时 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + String oldP = pendingClearArea.remove(uid); + plugin.getLogger().info( + "[防护-传送-进入] 取消旧延时, 旧区域=" + + oldP); + if (oldP != null) { + diffEffects(p, uid, oldP, toName); + } + } + + // 进入提示 + if (toArea != null && toArea.enterMsg != null + && !toArea.enterMsg.isEmpty()) { + p.sendMessage(formatAreaMsg(toArea.enterMsg)); + } + + // 发放效果 + if (toArea != null) { + /* plugin.getLogger().info( + "[防护-传送-进入] 调用applyRegionEffects");*/ + applyRegionEffects(p, uid, toName, toArea); +// 清除效果 + clearBadEffects(p, toArea); + + // ★ 没收违禁品(使用统一的handleConfiscate) + if (!isExemptFromConfiscation(p, toName)) { + handleConfiscate(p, toArea); + } + } + + // 强制模式 + if (toArea != null && toArea.enforceGameMode != null + && !toArea.enforceGameMode.isEmpty() + && !isExemptFromGameMode(p, toName)) { + GameMode target = GameMode.valueOf( + toArea.enforceGameMode); if (p.getGameMode() != target) { p.setGameMode(target); } } } + // 更新记录 + if (toName != null) { + playerCurrentArea.put(uid, toName); + } else { + playerCurrentArea.remove(uid); + } + } + + + public Map getPendingClearTasks() { + return pendingClearTask; + } + + public Map> + getPlayerMarkedEffects() { + return playerMarkedEffects; + } + + public void handleInside(Player p, AreaConfig ac) { + clearBadEffects(p, ac); + + if (ac.peaceMode) { + banHostilesWithWhitelist(p, ac); + } else if (ac.denyRaid) { + banRaidMobs(p, ac); + } + + // 全白或区白 → 跳过所有限制性规则(包括没收) if (isPlayerWhitelisted(p.getName(), ac)) return; + handleConfiscate(p, ac); } @@ -1839,6 +2914,16 @@ public class AreaProtection implements Listener { return areas.keySet(); } + /** + * 获取玩家当前所在区域名 + */ + public String getPlayerArea(Player p) { + return playerCurrentArea.get(p.getUniqueId()); + } + + public Collection getPendingTasks() { + return pendingClearTask.values(); + } // 判断是否为远程攻击敌对生物 @@ -2157,6 +3242,70 @@ public class AreaProtection implements Listener { it.remove(); } } + public void loadWhitelists() { + globalPlayerWhitelist.clear(); + areaPlayerWhitelist.clear(); + + if (!whitelistDir.exists()) { + whitelistDir.mkdirs(); + return; + } + + try { + // 加载全局白名单 + if (globalWhitelistFile.exists()) { + List lines = + java.nio.file.Files.readAllLines( + globalWhitelistFile.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + globalPlayerWhitelist.add(name); + } + } + } + + // 加载各区域白名单 + File[] files = whitelistDir.listFiles( + (File d, String n) -> n.endsWith(".txt") + && !n.equals("全局白名单.txt")); + if (files != null) { + for (File f : files) { + String areaName = f.getName() + .replace(".txt", ""); + Set set = + ConcurrentHashMap.newKeySet(); + List lines = + java.nio.file.Files.readAllLines( + f.toPath(), + StandardCharsets.UTF_8); + for (String line : lines) { + String name = line.trim(); + if (!name.isEmpty() + && !name.startsWith("#")) { + set.add(name); + } + } + if (!set.isEmpty()) { + areaPlayerWhitelist.put( + areaName, set); + } + } + } + + plugin.getLogger().info( + "[防护] 白名单加载完成,全局: " + + globalPlayerWhitelist.size() + + ",区域数: " + + areaPlayerWhitelist.size()); + } catch (Exception e) { + plugin.getLogger().warning( + "[防护] 加载白名单失败: " + + e.getMessage()); + } + } @EventHandler @@ -2302,7 +3451,6 @@ public class AreaProtection implements Listener { } // ==================== 管理命令 ==================== - public boolean handleCommand(CommandSender sender, String[] args) { args = smartReorderArgs(args); @@ -2311,8 +3459,76 @@ public class AreaProtection implements Listener { return true; } String sub = args[0].toLowerCase(); +// ===== 诊断命令 ===== + if (sub.equals("sdf1debug")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); - // 工具 + sender.sendMessage("§e===== 调试信息 ====="); + sender.sendMessage("§7当前区域: " + + playerCurrentArea.get(uid)); + sender.sendMessage("§7标记效果数: " + + (playerMarkedEffects.containsKey(uid) + ? playerMarkedEffects.get(uid).size() + : "无标记")); + sender.sendMessage("§7待清理任务: " + + pendingClearTask.containsKey(uid)); + sender.sendMessage("§7区域数: " + areas.size()); + + if (playerMarkedEffects.containsKey(uid)) { + List marked = + playerMarkedEffects.get(uid); + for (PotionEffectType type : marked) { + sender.sendMessage("§7 标记: " + + type.getName() + + " 身上有=" + + (p.getPotionEffect(type) != null)); + } + } + return true; + } + +// ===== 强制清理命令(测试用)===== + if (sub.equals("testclear")) { + if (!(sender instanceof Player)) { + sender.sendMessage("§c仅玩家可用"); + return true; + } + Player p = (Player) sender; + UUID uid = p.getUniqueId(); + + List marked = + playerMarkedEffects.remove(uid); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + sender.sendMessage("§a已强制清理 " + + marked.size() + " 个效果"); + } else { + sender.sendMessage("§c无标记效果可清理"); + } + + // 取消延时任务 + if (pendingClearTask.containsKey(uid)) { + pendingClearTask.get(uid).cancel(); + pendingClearTask.remove(uid); + pendingClearArea.remove(uid); + pendingClearEffects.remove(uid); + sender.sendMessage("§a已取消延时任务"); + } + + removePlayerEffects(uid, + playerCurrentArea.getOrDefault(uid, "")); + sender.sendMessage("§aDB已清理"); + return true; + } + + // ===== 工具 ===== if (sub.equals("工具") || sub.equals("wand")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); @@ -2323,17 +3539,14 @@ public class AreaProtection implements Listener { ItemMeta meta = tool.getItemMeta(); meta.setDisplayName("§a§l区域选择工具"); meta.setLore(Arrays.asList( - "§7左键: 位置1", - "§7右键: 位置2")); + "§7左键: 位置1", "§7右键: 位置2")); tool.setItemMeta(meta); p.getInventory().addItem(tool); - p.sendMessage("§a§l[防护] §f已获取选择工具(旋风棒)"); - p.sendMessage("§7左键方块=位置1,右键方块=位置2"); + p.sendMessage("§a§l[防护] §f已获取选择工具"); return true; } - - // 创建 + // ===== 创建 ===== if (sub.equals("创建") || sub.equals("create")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); @@ -2352,7 +3565,7 @@ public class AreaProtection implements Listener { return true; } String areaName = args[1]; - File f = new File(areaDir, areaName + ".txt"); + File f = new File(rootDir, areaName + ".txt"); if (f.exists()) { p.sendMessage("§c区域已存在"); return true; @@ -2373,217 +3586,194 @@ public class AreaProtection implements Listener { + "," + l2.getBlockZ()); pw.println("高度范围: 0-255"); pw.println(); - pw.println("# 没收物品: 蘑菇,毒蘑菇"); pw.println("# 禁止放置方块"); pw.println("# 禁止破坏方块"); pw.println("# 禁止PVP"); - pw.println("# 禁止摔伤"); - pw.println("# 禁止饥饿"); - pw.println("# 禁止一切伤害"); - pw.println("# 禁止丢弃物品"); - pw.println("# 禁止末影珍珠"); - pw.println("# 禁止使用弓箭"); - pw.println("# 禁止骑乘"); - pw.println("# 禁止袭击"); - pw.println("# 禁止药水"); - pw.println("# 禁止爆炸"); - pw.println("# 禁止使用物品: 蘑菇,毒蘑菇"); pw.println("# 效果: 夜视 1 999"); pw.println("# 进入提示: 欢迎来到保护区"); pw.println("# 离开提示: 已离开保护区"); - pw.println("# 没收提示: 你的违禁品已被没收"); - pw.println("# 通报批评: {player} 在保护区违规!"); - pw.println("# 惩罚命令: ban {player} 60s 违规"); pw.close(); - p.sendMessage("§a§l[防护] §f区域 " + areaName + " 已创建"); + p.sendMessage("§a§l[防护] §f区域 " + + areaName + " 已创建"); loadAllAreas(); } catch (IOException ex) { p.sendMessage("§c创建失败"); } return true; } -// ===== 和平白名单:addname / removename ===== + + // ===== 和平白名单 ===== if (sub.equals("addname")) { if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别: " + + args[1] + " 或 " + args[2]); return true; } - String areaName = parsed[0]; - String creatureName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(parsed[0]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - if (ac.peaceWhitelist.contains(creatureName)) { - sender.sendMessage("§e" + creatureName + " 已在 " + areaName + " 和平白名单中"); - return true; - } - ac.peaceWhitelist.add(creatureName); - savePeaceWhitelist(ac); - sender.sendMessage("§a已添加: " + creatureName + " → " + areaName + " 和平白名单"); + ac.peaceWhitelist.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 和平白名单"); return true; } - sender.sendMessage("§c用法: /protect addname <区域/名字> <区域/名字>"); + sender.sendMessage("§c用法: /protect addname " + + "<区域> <名字>"); return true; } - - if (sub.equals("removename")) { if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别"); return true; } - String areaName = parsed[0]; - String creatureName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(parsed[0]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - if (!ac.peaceWhitelist.contains(creatureName)) { - sender.sendMessage("§e" + creatureName + " 不在 " + areaName + " 和平白名单中"); - return true; - } - ac.peaceWhitelist.remove(creatureName); - savePeaceWhitelist(ac); - sender.sendMessage("§a已移除: " + creatureName + " ← " + areaName + " 和平白名单"); + ac.peaceWhitelist.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + parsed[0]); return true; } - sender.sendMessage("§c用法: /protect removename <区域/名字> <区域/名字>"); + sender.sendMessage("§c用法: /protect removename " + + "<区域> <名字>"); return true; } - - if (sub.equals("listname")) { if (args.length < 2) { - sender.sendMessage("§c用法: /protect listname <区域>"); + sender.sendMessage( + "§c用法: /protect listname <区域>"); return true; } - String areaName = args[1]; - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(args[1]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - sender.sendMessage("§a" + areaName + " 和平白名单:"); + sender.sendMessage("§a" + args[1] + + " 和平白名单:"); if (ac.peaceWhitelist.isEmpty()) { sender.sendMessage("§7 (空)"); } else { - for (String name : ac.peaceWhitelist) { - sender.sendMessage("§7 - " + name); + for (String n : ac.peaceWhitelist) { + sender.sendMessage("§7 - " + n); } } return true; } -// ===== 模式排除:addwhite / removewhite ===== + // ===== 模式排除 ===== if (sub.equals("addwhite")) { if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别"); return true; } - String areaName = parsed[0]; - String playerName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(parsed[0]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - Set global = globalWhitelist.get("global"); - if (global != null && global.contains(playerName.toLowerCase())) { - sender.sendMessage("§c" + playerName + " 已在全局白名单中,不需要添加到模式排除"); - return true; - } - Set areaWl = areaWhitelist.get(areaName); - if (areaWl != null && areaWl.contains(playerName.toLowerCase())) { - sender.sendMessage("§c" + playerName + " 已在" + areaName + "白名单中,不需要添加到模式排除"); - return true; - } - if (ac.modeExempt.contains(playerName)) { - sender.sendMessage("§c" + playerName + " 已在" + areaName + "模式排除名单中"); - return true; - } - ac.modeExempt.add(playerName); - saveModeExempt(ac); - sender.sendMessage("§a已添加: " + playerName + " → " + areaName + " 模式排除名单"); + ac.modeExempt.add(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已添加: " + + parsed[1] + " → " + + parsed[0] + " 模式排除"); return true; } - sender.sendMessage("§c用法: /protect addwhite <区域/玩家> <区域/玩家>"); + sender.sendMessage("§c用法: /protect addwhite " + + "<区域> <玩家>"); return true; } - if (sub.equals("removewhite")) { if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别"); return true; } - String areaName = parsed[0]; - String playerName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(parsed[0]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - if (!ac.modeExempt.contains(playerName)) { - sender.sendMessage("§c" + playerName + " 不在 " + areaName + " 模式排除名单中"); - return true; - } - ac.modeExempt.remove(playerName); - saveModeExempt(ac); - sender.sendMessage("§a已移除: " + playerName + " ← " + areaName + " 模式排除名单"); + ac.modeExempt.remove(parsed[1]); + saveAreaConfig(ac); + sender.sendMessage("§a已移除: " + + parsed[1] + " ← " + + parsed[0] + " 模式排除"); return true; } - sender.sendMessage("§c用法: /protect removewhite <区域/玩家> <区域/玩家>"); + sender.sendMessage("§c用法: /protect removewhite " + + "<区域> <玩家>"); return true; } - if (sub.equals("listwhite")) { if (args.length < 2) { - sender.sendMessage("§c用法: /protect listwhite <区域>"); + sender.sendMessage( + "§c用法: /protect listwhite <区域>"); return true; } - String areaName = args[1]; - AreaConfig ac = areas.get(areaName); + AreaConfig ac = areas.get(args[1]); if (ac == null) { - sender.sendMessage("§c区域不存在: " + areaName); + sender.sendMessage("§c区域不存在"); return true; } - sender.sendMessage("§a" + areaName + " 模式排除名单:"); + sender.sendMessage("§a" + args[1] + + " 模式排除名单:"); if (ac.modeExempt.isEmpty()) { sender.sendMessage("§7 (空)"); } else { - for (String name : ac.modeExempt) { - sender.sendMessage("§7 - " + name); + for (String n : ac.modeExempt) { + sender.sendMessage("§7 - " + n); } } return true; } - - - // 列表 + // ===== 列表 ===== if (sub.equals("列表") || sub.equals("list")) { + if (args.length == 2) { + if (args[1].equalsIgnoreCase("global")) { + sender.sendMessage("§a全局白名单:"); + if (globalPlayerWhitelist.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n + : globalPlayerWhitelist) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } + String an = args[1]; + Set wl = + areaPlayerWhitelist.get(an); + sender.sendMessage("§a" + an + " 白名单:"); + if (wl == null || wl.isEmpty()) { + sender.sendMessage("§7 (空)"); + } else { + for (String n : wl) { + sender.sendMessage("§7 - " + n); + } + } + return true; + } if (areas.isEmpty()) { sender.sendMessage("§7暂无区域"); } else { @@ -2592,91 +3782,151 @@ public class AreaProtection implements Listener { : areas.entrySet()) { sender.sendMessage("§a " + en.getKey() + " §7规则:" - + en.getValue().ruleCount() + "条"); + + en.getValue().ruleCount() + + "条"); } } return true; } - // 重载 + // ===== 重载 ===== if (sub.equals("重载") || sub.equals("reload")) { reload(); - sender.sendMessage("§a已重载 " + areas.size() + " 个区域"); + sender.sendMessage("§a已重载 " + + areas.size() + " 个区域"); return true; } -// ===== list:列出白名单 ===== - if (sub.equals("list")) { + + // ===== 物品黑名单 ===== + if (sub.equals("additem")) { if (args.length == 2) { - String areaName = resolveAreaName(args[1]); - if (areaName == null || areaName.equalsIgnoreCase("global")) { - sender.sendMessage("§a全局白名单:"); - Set wl = globalWhitelist.get("global"); - if (wl == null || wl.isEmpty()) { - sender.sendMessage("§7 (空)"); - } else { - for (String name : wl) { - sender.sendMessage("§7 - " + name); - } - } + String itemName = args[1].toUpperCase(); + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + args[1]); return true; } - if (!areaName.equals(args[1])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - Set wl = areaWhitelist.get(areaName); - sender.sendMessage("§a" + areaName + " 白名单:"); - if (wl == null || wl.isEmpty()) { - sender.sendMessage("§7 (空)"); + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + return true; + } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); } else { - for (String name : wl) { - sender.sendMessage("§7 - " + name); - } + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (Material.matchMaterial(itemName) + == null) { + sender.sendMessage( + "§c无效物品ID: " + itemName); + return true; + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.add(itemName); + sender.sendMessage("§a已全局加黑: " + + itemName); + } else { + areaItemBlacklist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()) + .add(itemName); + sender.sendMessage("§a已加黑: " + + itemName + " → " + areaName); } return true; } - sender.sendMessage("§a全局白名单:"); - Set wl = globalWhitelist.get("global"); - if (wl == null || wl.isEmpty()) { - sender.sendMessage("§7 (空)"); - } else { - for (String name : wl) { - sender.sendMessage("§7 - " + name); + sender.sendMessage("§c用法: /protect additem " + + "<区域/物品> <区域/物品>"); + return true; + } + if (sub.equals("removeitem")) { + if (args.length == 2) { + String itemName = args[1].toUpperCase(); + if (!globalItemBlacklist + .contains(itemName)) { + sender.sendMessage( + "§c全局黑名单中没有: " + + itemName); + return true; } + globalItemBlacklist.remove(itemName); + sender.sendMessage("§a已移除: " + itemName); + return true; } + if (args.length == 3) { + String r1 = resolveAreaName(args[1]); + String r2 = resolveAreaName(args[2]); + String areaName; + String itemName; + if (r1 != null) { + areaName = r1; + itemName = args[2].toUpperCase(); + } else if (r2 != null) { + areaName = r2; + itemName = args[1].toUpperCase(); + } else { + areaName = "global"; + itemName = args[1].toUpperCase(); + } + if (areaName.equalsIgnoreCase("global")) { + globalItemBlacklist.remove(itemName); + } else { + Set list = + areaItemBlacklist.get(areaName); + if (list != null) { + list.remove(itemName); + } + } + sender.sendMessage("§a已移除: " + + itemName); + return true; + } + sender.sendMessage("§c用法: /protect removeitem " + + "<区域/物品> <区域/物品>"); return true; } - - -// ===== listitem:列出黑名单物品 ===== if (sub.equals("listitem")) { if (args.length < 2) { - // 全局黑名单 - sender.sendMessage("§a§l===== 全局物品黑名单 ====="); + sender.sendMessage( + "§a全局物品黑名单:"); if (globalItemBlacklist.isEmpty()) { sender.sendMessage("§7(空)"); } else { - for (String item : globalItemBlacklist) { + for (String item + : globalItemBlacklist) { sender.sendMessage("§f- " + item); } } return true; } - // 区域黑名单 - String area = args[1]; - if (!validateArea(sender, area)) return true; - Set list = areaItemBlacklist.get(area); - sender.sendMessage("§a§l===== " + area + " 物品黑名单 ====="); + Set list = + areaItemBlacklist.get(args[1]); + sender.sendMessage("§a" + args[1] + + " 物品黑名单:"); if (list == null || list.isEmpty()) { sender.sendMessage("§7(空)"); - return true; - } - for (String item : list) { - sender.sendMessage("§f- " + item); + } else { + for (String item : list) { + sender.sendMessage("§f- " + item); + } } return true; } -// ===== expand 扩大选区 ===== + // ===== expand 扩建 ===== if (sub.equals("expand")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); @@ -2698,40 +3948,30 @@ public class AreaProtection implements Listener { amount = Integer.parseInt(args[1]); } catch (NumberFormatException ignored) {} } - - // 获取区域当前边界 int minX = Math.min(ac.x1, ac.x2); int maxX = Math.max(ac.x1, ac.x2); int minZ = Math.min(ac.z1, ac.z2); int maxZ = Math.max(ac.z1, ac.z2); - - // 根据面朝方向扩建 float yaw = p.getLocation().getYaw(); String dir = yawToDir(yaw); - switch (dir) { - case "北": minZ -= amount; break; - case "南": maxZ += amount; break; - case "东": maxX += amount; break; - case "西": minX -= amount; break; - } - - // 写回配置文件 + if (dir.equals("北")) minZ -= amount; + else if (dir.equals("南")) maxZ += amount; + else if (dir.equals("东")) maxX += amount; + else if (dir.equals("西")) minX -= amount; ac.x1 = minX; ac.x2 = maxX; ac.z1 = minZ; ac.z2 = maxZ; saveAreaConfig(ac); - p.sendMessage("§a§l[防护] §f区域 §e" + ac.name - + " §f已向 §e" + dir + " §f扩建 §e" + amount + "§f格"); - p.sendMessage("§7新范围: X(" + minX + "~" + maxX - + ") Z(" + minZ + "~" + maxZ + ")"); + + " §f向 §e" + dir + " §f扩建 §e" + + amount + "§f格"); return true; } - -// ===== contraction 收缩选区 ===== - if (sub.equals("contraction") || sub.equals("contract")) { + // ===== contract 收缩 ===== + if (sub.equals("contraction") + || sub.equals("contract")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); return true; @@ -2752,46 +3992,39 @@ public class AreaProtection implements Listener { amount = Integer.parseInt(args[1]); } catch (NumberFormatException ignored) {} } - int minX = Math.min(ac.x1, ac.x2); int maxX = Math.max(ac.x1, ac.x2); int minZ = Math.min(ac.z1, ac.z2); int maxZ = Math.max(ac.z1, ac.z2); - float yaw = p.getLocation().getYaw(); String dir = yawToDir(yaw); - switch (dir) { - case "北": minZ += amount; break; - case "南": maxZ -= amount; break; - case "东": maxX -= amount; break; - case "西": minX += amount; break; - } - + if (dir.equals("北")) minZ += amount; + else if (dir.equals("南")) maxZ -= amount; + else if (dir.equals("东")) maxX -= amount; + else if (dir.equals("西")) minX += amount; if (minX > maxX || minZ > maxZ) { - p.sendMessage("§c收缩过度,区域已无效"); + p.sendMessage("§c收缩过度"); return true; } - ac.x1 = minX; ac.x2 = maxX; ac.z1 = minZ; ac.z2 = maxZ; saveAreaConfig(ac); - p.sendMessage("§a§l[防护] §f区域 §e" + ac.name - + " §f已向 §e" + dir + " §f收缩 §e" + amount + "§f格"); - p.sendMessage("§7新范围: X(" + minX + "~" + maxX - + ") Z(" + minZ + "~" + maxZ + ")"); + + " §f向 §e" + dir + " §f收缩 §e" + + amount + "§f格"); return true; } - // 删除 + // ===== 删除 ===== if (sub.equals("删除") || sub.equals("delete")) { if (args.length < 2) { - sender.sendMessage("§e用法: /protect 删除 <名>"); + sender.sendMessage( + "§e用法: /protect 删除 <名>"); return true; } - File f = new File(areaDir, args[1] + ".txt"); + File f = new File(rootDir, args[1] + ".txt"); if (f.exists()) { f.delete(); areas.remove(args[1]); @@ -2802,263 +4035,98 @@ public class AreaProtection implements Listener { return true; } - // ===== add 玩家白名单 ===== + // ===== add 加白名单 ===== if (sub.equals("add")) { if (args.length == 2) { - String playerName = args[1]; - if (!playerExists(playerName)) { - sender.sendMessage("§c玩家不存在: " + playerName); + String name = args[1]; + if (globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 已在全局白名单中"); return true; } - Set wl = globalWhitelist - .computeIfAbsent("global", k -> new HashSet<>()); - if (wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 已在全局白名单中"); - return true; - } - wl.add(playerName.toLowerCase()); - saveGlobalWhitelist(); - sender.sendMessage("§a已全局加白: " + playerName); + globalPlayerWhitelist.add(name); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + name); return true; } if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别"); return true; } String areaName = parsed[0]; String playerName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - if (!playerExists(playerName)) { - sender.sendMessage("§c玩家不存在: " + playerName); - return true; - } if (areaName.equalsIgnoreCase("global")) { - Set wl = globalWhitelist - .computeIfAbsent("global", k -> new HashSet<>()); - if (wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 已在全局白名单中"); - return true; - } - wl.add(playerName.toLowerCase()); - saveGlobalWhitelist(); - sender.sendMessage("§a已全局加白: " + playerName); + globalPlayerWhitelist.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已全局加白: " + + playerName); } else { - Set wl = areaWhitelist - .computeIfAbsent(areaName, k -> new HashSet<>()); - if (wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 已在 " + areaName + " 白名单中"); - return true; - } - wl.add(playerName.toLowerCase()); - saveAreaWhitelist(areaName, "whitelist", wl); - sender.sendMessage("§a已加白: " + playerName + " → " + areaName); + Set wl = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap + .newKeySet()); + wl.add(playerName); + saveWhitelists(); + sender.sendMessage("§a已加白: " + + playerName + " → " + areaName); } return true; } - sender.sendMessage("§c用法: /protect add <区域/玩家> <区域/玩家>"); + sender.sendMessage("§c用法: /protect add " + + "<区域/玩家> <区域/玩家>"); return true; } -// remove 命令 + // ===== remove 移除白名单 ===== if (sub.equals("remove")) { if (args.length == 2) { - String playerName = args[1]; - Set wl = globalWhitelist.get("global"); - if (wl == null || !wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 不在全局白名单中"); + String name = args[1]; + if (!globalPlayerWhitelist.contains(name)) { + sender.sendMessage("§e" + name + + " 不在全局白名单中"); return true; } - wl.remove(playerName.toLowerCase()); - saveGlobalWhitelist(); - sender.sendMessage("§a已从全局白名单移除: " + playerName); + globalPlayerWhitelist.remove(name); + saveWhitelists(); + sender.sendMessage("§a已从全局白名单移除: " + + name); return true; } if (args.length == 3) { - String[] parsed = parseAreaAndTarget(args[1], args[2]); + String[] parsed = parseAreaAndTarget( + args[1], args[2]); if (parsed == null) { - sender.sendMessage("§c无法识别区域名: " + args[1] + " 或 " + args[2]); + sender.sendMessage("§c无法识别"); return true; } String areaName = parsed[0]; String playerName = parsed[1]; - if (!areaName.equals(args[1]) && !areaName.equals(args[2])) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } if (areaName.equalsIgnoreCase("global")) { - Set wl = globalWhitelist.get("global"); - if (wl == null || !wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 不在全局白名单中"); - return true; - } - wl.remove(playerName.toLowerCase()); - saveGlobalWhitelist(); - sender.sendMessage("§a已从全局白名单移除: " + playerName); + globalPlayerWhitelist.remove(playerName); + saveWhitelists(); + sender.sendMessage("§a已移除: " + + playerName); } else { - Set wl = areaWhitelist.get(areaName); - if (wl == null || !wl.contains(playerName.toLowerCase())) { - sender.sendMessage("§e" + playerName + " 不在 " + areaName + " 白名单中"); - return true; + Set wl = + areaPlayerWhitelist.get(areaName); + if (wl != null) { + wl.remove(playerName); } - wl.remove(playerName.toLowerCase()); - saveAreaWhitelist(areaName, "whitelist", wl); - sender.sendMessage("§a已从 " + areaName + " 白名单移除: " + playerName); + saveWhitelists(); + sender.sendMessage("§a已从 " + areaName + + " 移除: " + playerName); } return true; } - sender.sendMessage("§c用法: /protect remove <区域/玩家> <区域/玩家>"); + sender.sendMessage("§c用法: /protect remove " + + "<区域/玩家> <区域/玩家>"); return true; } - - if (sub.equals("additem")) { - if (args.length == 2) { - String itemName = args[1].toUpperCase(); - if (Material.matchMaterial(itemName) == null) { - sender.sendMessage("§c无效的物品ID: " + args[1]); - return true; - } - globalItemBlacklist.add(itemName); - saveGlobalWhitelist(); - sender.sendMessage("§a已全局加黑: " + itemName); - return true; - } - if (args.length == 3) { - // 先尝试两边都解析区域名 - String r1 = resolveAreaName(args[1]); - String r2 = resolveAreaName(args[2]); - - String areaName = null; - String itemName = null; - - if (r1 != null && r2 == null) { - // arg1是区域名,arg2是物品 - areaName = r1; - itemName = args[2].toUpperCase(); - } else if (r2 != null && r1 == null) { - // arg2是区域名,arg1是物品 - areaName = r2; - itemName = args[1].toUpperCase(); - } else if (r1 != null && r2 != null) { - // 两边都是区域名,第一个优先 - areaName = r1; - itemName = args[2].toUpperCase(); - } else { - // 都不是区域名,默认arg1是物品,arg2是区域名 - // 也尝试 arg1 当区域名模糊匹配 - itemName = args[1].toUpperCase(); - areaName = r2 != null ? r2 : "global"; - } - - // 验证物品ID - if (Material.matchMaterial(itemName) == null) { - // 再试一次:可能 arg2 才是物品 - itemName = args[2].toUpperCase(); - if (areaName.equals(args[2])) { - areaName = r1 != null ? r1 : "global"; - } - if (Material.matchMaterial(itemName) == null) { - sender.sendMessage("§c无效的物品ID: " + args[1] + " 和 " + args[2]); - return true; - } - } - - // 纠正提示 - if (r1 != null && !r1.equals(args[1]) && areaName.equals(r1)) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - if (r2 != null && !r2.equals(args[2]) && areaName.equals(r2)) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - - if (areaName.equalsIgnoreCase("global")) { - globalItemBlacklist.add(itemName); - saveGlobalWhitelist(); - sender.sendMessage("§a已全局加黑: " + itemName); - } else { - areaItemBlacklist - .computeIfAbsent(areaName, k -> ConcurrentHashMap.newKeySet()) - .add(itemName); - saveAreaWhitelist(areaName, "item", - areaItemBlacklist.get(areaName)); - sender.sendMessage("§a已加黑: " + itemName + " → " + areaName); - } - return true; - } - sender.sendMessage("§c用法: /protect additem <区域/物品> <区域/物品>"); - return true; - } - - if (sub.equals("removeitem")) { - if (args.length == 2) { - String itemName = args[1].toUpperCase(); - if (!globalItemBlacklist.contains(itemName)) { - sender.sendMessage("§c全局黑名单中没有: " + itemName); - return true; - } - globalItemBlacklist.remove(itemName); - saveGlobalWhitelist(); - sender.sendMessage("§a已从全局黑名单移除: " + itemName); - return true; - } - if (args.length == 3) { - String r1 = resolveAreaName(args[1]); - String r2 = resolveAreaName(args[2]); - - String areaName = null; - String itemName = null; - - if (r1 != null && r2 == null) { - areaName = r1; - itemName = args[2].toUpperCase(); - } else if (r2 != null && r1 == null) { - areaName = r2; - itemName = args[1].toUpperCase(); - } else if (r1 != null && r2 != null) { - areaName = r1; - itemName = args[2].toUpperCase(); - } else { - itemName = args[1].toUpperCase(); - areaName = "global"; - } - - // 纠正提示 - if (r1 != null && !r1.equals(args[1]) && areaName.equals(r1)) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - if (r2 != null && !r2.equals(args[2]) && areaName.equals(r2)) { - sender.sendMessage("§7区域名已自动纠正为: §f" + areaName); - } - - if (areaName.equalsIgnoreCase("global")) { - if (!globalItemBlacklist.contains(itemName)) { - sender.sendMessage("§c全局黑名单中没有: " + itemName); - return true; - } - globalItemBlacklist.remove(itemName); - saveGlobalWhitelist(); - sender.sendMessage("§a已从全局黑名单移除: " + itemName); - } else { - Set list = areaItemBlacklist.get(areaName); - if (list == null || !list.contains(itemName)) { - sender.sendMessage("§c" + areaName + " 黑名单中没有: " + itemName); - return true; - } - list.remove(itemName); - saveAreaWhitelist(areaName, "item", list); - sender.sendMessage("§a已从 " + areaName + " 黑名单移除: " + itemName); - } - return true; - } - sender.sendMessage("§c用法: /protect removeitem <区域/物品> <区域/物品>"); - return true; - } - - - // ===== on 显示边框 ===== + // ===== on/off/tempon 边框 ===== if (sub.equals("on")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); @@ -3075,23 +4143,20 @@ public class AreaProtection implements Listener { return true; } showBorder(p, ac, false); - p.sendMessage("§a§l[防护] §f已显示 " + ac.name + " 边框"); + p.sendMessage("§a§l[防护] §f已显示 " + + ac.name + " 边框"); return true; } - - // ===== off 关闭边框 ===== if (sub.equals("off")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); return true; } - Player p = (Player) sender; - cancelBorder(p); - p.sendMessage("§a§l[防护] §f已关闭边框显示"); + cancelBorder((Player) sender); + ((Player) sender).sendMessage( + "§a§l[防护] §f已关闭边框显示"); return true; } - - // ===== tempon 临时显示边框 ===== if (sub.equals("tempon")) { if (!(sender instanceof Player)) { sender.sendMessage("§c仅玩家可用"); @@ -3116,6 +4181,8 @@ public class AreaProtection implements Listener { showHelp(sender); return true; } + + private String[] parseAreaAndTarget(String arg1, String arg2) { String r1 = resolveAreaName(arg1); if (r1 != null) { @@ -3170,7 +4237,7 @@ public class AreaProtection implements Listener { return "东"; } private void saveAreaConfig(AreaConfig ac) { - File f = new File(areaDir, ac.name + ".txt"); + File f = new File(rootDir, ac.name + ".txt"); if (!f.exists()) return; try { List lines = new ArrayList<>(); @@ -3218,10 +4285,64 @@ public class AreaProtection implements Listener { s.sendMessage("§a/protect list <区域> §7列出区域白名单"); s.sendMessage("§a/protect listitem §7列出全局物品黑名单"); s.sendMessage("§a/protect listitem <区域> §7列出区域物品黑名单"); + s.sendMessage("§b§l欢迎游玩草原探险服务器"); + s.sendMessage("§b§l服务器ip:mc2.ypshidifu.cn\n端口30679"); + s.sendMessage(""); } +// ==================== 白名单管理公共方法 ==================== + + // ==================== 白名单操作 ==================== + + public boolean isPlayerGlobalWhitelisted( + String playerName) { + return globalPlayerWhitelist.contains(playerName); + } + + public boolean addPlayerToGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromGlobalWhitelist( + String playerName) { + boolean ok = globalPlayerWhitelist.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean isPlayerAreaWhitelisted( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + return wl != null && wl.contains(playerName); + } + + public boolean addPlayerToAreaWhitelist( + String areaName, String playerName) { + Set set = areaPlayerWhitelist + .computeIfAbsent(areaName, + k -> ConcurrentHashMap.newKeySet()); + boolean ok = set.add(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public boolean removePlayerFromAreaWhitelist( + String areaName, String playerName) { + Set wl = + areaPlayerWhitelist.get(areaName); + boolean ok = wl != null && wl.remove(playerName); + if (ok) saveWhitelists(); + return ok; + } + + public int handleConfiscate(Player p, AreaConfig ac) { + if (isPlayerWhitelisted(p.getName(), ac)) return 0; Set allItems = new HashSet<>(ac.confiscateItems); allItems.addAll(globalItemBlacklist); Set areaList = areaItemBlacklist.get(ac.name); @@ -3270,6 +4391,11 @@ public class AreaProtection implements Listener { && !ac.confiscateMsg.isEmpty()) ? ac.confiscateMsg : "你携带了违禁品,已被没收"; + // ★ 替换变量 + msg = msg.replace("{player}", p.getName()) + .replace("{area}", ac.name) + .replace("{count}", String.valueOf(totalRemoved)) + .replace("{items}", confiscatedNames); p.sendMessage("§c§l[区域防护] §f" + msg); p.sendMessage("§7没收物品: " + confiscatedNames); @@ -3563,7 +4689,7 @@ public class AreaProtection implements Listener { private void savePeaceWhitelist(AreaConfig ac) { - File file = new File(areaDir, ac.name + ".txt"); + File file = new File(rootDir, ac.name + ".txt"); if (!file.exists()) return; try { diff --git a/src/main/java/Sdf1_login/BondManager.java b/src/main/java/Sdf1_login/BondManager.java index ea78152..5122223 100644 --- a/src/main/java/Sdf1_login/BondManager.java +++ b/src/main/java/Sdf1_login/BondManager.java @@ -21,6 +21,8 @@ public class BondManager { public static final String TX_DAILY_SIGN = "daily_sign"; public static final String TX_FREEZE = "freeze"; public static final String TX_UNFREEZE = "unfreeze"; + public static final String TX_TICKET_REWARD = "ticket_reward"; + public static final String TX_TICKET_SERVICE = "ticket_service"; public static final int MAX_WATER_DAYS = 14; diff --git a/src/main/java/Sdf1_login/BondPrinter.java b/src/main/java/Sdf1_login/BondPrinter.java index 674ec3c..da3c473 100644 --- a/src/main/java/Sdf1_login/BondPrinter.java +++ b/src/main/java/Sdf1_login/BondPrinter.java @@ -37,6 +37,8 @@ public class BondPrinter { TYPE_NAMES.put("daily_sign", "签到奖励"); TYPE_NAMES.put("freeze", "冻结"); TYPE_NAMES.put("unfreeze", "解冻"); + TYPE_NAMES.put("ticket_reward", "工单奖励"); + TYPE_NAMES.put("ticket_service", "工单服务奖励"); } diff --git a/src/main/java/Sdf1_login/CDKManager.java b/src/main/java/Sdf1_login/CDKManager.java index e07569f..481072d 100644 --- a/src/main/java/Sdf1_login/CDKManager.java +++ b/src/main/java/Sdf1_login/CDKManager.java @@ -164,6 +164,12 @@ public class CDKManager { if ("bond".equals(input.type)) { handleBondInput(p, msg.trim(), input.context); + return; + } + + if ("points".equals(input.type)) { + handlePointsInput(p, msg.trim(), + input.context); } } @@ -281,6 +287,41 @@ public class CDKManager { } } + private void handlePointsInput(Player admin, + String msg, + String target) { + if (!msg.startsWith("+") + && !msg.startsWith("-")) { + admin.sendMessage("§c格式: §a+100" + + " §7或 §c-50"); + return; + } + try { + String sign = msg.substring(0, 1); + String rest = + msg.substring(1).trim(); + int amount = Integer.parseInt(rest); + if (amount <= 0) { + admin.sendMessage("§c金额必须>0"); + return; + } + if ("-".equals(sign)) { + if (plugin.getDb().deductPoints( + target, amount)) { + admin.sendMessage("§a扣除成功"); + } else { + admin.sendMessage("§c积分不足"); + } + } else { + plugin.getDb().addPoints( + target, amount); + admin.sendMessage("§a发放成功"); + } + } catch (NumberFormatException e) { + admin.sendMessage("§c格式错误"); + } + } + public void cancelInput(String player) { pendingInputs.remove(player); } diff --git a/src/main/java/Sdf1_login/CheckInManager.java b/src/main/java/Sdf1_login/CheckInManager.java index b594b0f..b8e612c 100644 --- a/src/main/java/Sdf1_login/CheckInManager.java +++ b/src/main/java/Sdf1_login/CheckInManager.java @@ -57,17 +57,20 @@ public class CheckInManager { long diff = todayD.getTime() - lastD.getTime(); if (diff > 86400000L * 2) { - streak = 0; + // 间隔超过2天,重置连续天数 + streak = 1; } else if (diff > 86400000L) { + // 间隔1-2天(连续签到),天数+1 streak++; } + // 间隔<=1天(同一天),不改变连续天数 } catch (Exception ignored) { } + } else { + // 首次签到 + streak = 1; } - if (streak == 0) streak = 1; - else streak++; - double multi = 1.0; if (streak >= 21) multi = 1.5; else if (streak >= 14) multi = 1.3; diff --git a/src/main/java/Sdf1_login/CypayCommand.java b/src/main/java/Sdf1_login/CypayCommand.java index 7fd9067..32c504f 100644 --- a/src/main/java/Sdf1_login/CypayCommand.java +++ b/src/main/java/Sdf1_login/CypayCommand.java @@ -241,7 +241,7 @@ public class CypayCommand } // ===== /cypay give/remove ===== - if (args.length == 3) { + if (args.length >= 3 && args.length <= 4) { String action = args[0].toLowerCase(); if ("give".equals(action) || "remove".equals(action)) { @@ -268,12 +268,18 @@ public class CypayCommand sender instanceof Player ? sender.getName() : "Console"; + // 自定义理由(可选) + String reason = args.length == 4 + ? args[3] + : ("give".equals(action) + ? "管理员发放" + : "管理员扣除"); if ("give".equals(action)) { plugin.getBonds().addBonds( target, amount, txType, "", - opName, "管理员发放"); + opName, reason); sender.sendMessage( "§a已给 " + target + " " + amount @@ -291,7 +297,7 @@ public class CypayCommand amount, txType, "", opName, - "管理员扣除")) { + reason)) { sender.sendMessage( "§a已扣除 " + target + " " + amount @@ -477,9 +483,9 @@ public class CypayCommand "§e/cypay <玩家> <金额> §7- 转账"); if (hasAdminPerm(sender)) { sender.sendMessage( - "§e/cypay give <玩家> <金额> §7- 给债券"); + "§e/cypay give <玩家> <金额> [理由] §7- 给债券"); sender.sendMessage( - "§e/cypay remove <玩家> <金额> §7- 扣债券"); + "§e/cypay remove <玩家> <金额> [理由] §7- 扣债券"); sender.sendMessage( "§e/cypay import <文件> §7- 导入CDK"); sender.sendMessage( @@ -572,6 +578,19 @@ public class CypayCommand } } + if (args.length == 4) { + String first = args[0].toLowerCase(); + if ("give".equals(first) + || "remove".equals(first)) { + list.add("管理员发放"); + list.add("管理员扣除"); + list.add("工单奖励"); + list.add("活动奖励"); + return filter(list, + args[3].toLowerCase()); + } + } + return list; } } diff --git a/src/main/java/Sdf1_login/DatabaseManager.java b/src/main/java/Sdf1_login/DatabaseManager.java index cb2b8f7..ea89540 100644 --- a/src/main/java/Sdf1_login/DatabaseManager.java +++ b/src/main/java/Sdf1_login/DatabaseManager.java @@ -82,6 +82,12 @@ public class DatabaseManager { "TEXT DEFAULT ''"); safeAdd(st, "register_type", "TEXT DEFAULT 'manual'"); + safeAdd(st, "login_streak", + "INTEGER DEFAULT 0"); + safeAdd(st, "last_login_date", + "TEXT DEFAULT ''"); + safeAdd(st, "last_login_ip", + "TEXT DEFAULT ''"); // ===== 背包备份表:安全迁移 ===== // 检查旧表是否存在(用 inventory_data 列判断) @@ -675,6 +681,8 @@ public class DatabaseManager { public void recordIP(String name, String ip) { if (ip != null && !ip.isEmpty()) { setField(name, "ip_address", ip); + // ★ 同时更新最后登录IP + setField(name, "last_login_ip", ip); } } diff --git a/src/main/java/Sdf1_login/GUIManager.java b/src/main/java/Sdf1_login/GUIManager.java index 2b0b2a2..7c23143 100644 --- a/src/main/java/Sdf1_login/GUIManager.java +++ b/src/main/java/Sdf1_login/GUIManager.java @@ -298,7 +298,6 @@ public class GUIManager implements Listener { for (int i = 0; i < list.size() && i < 9; i++) { MenuManager.MenuItem mi = list.get(i); - // ★ 直接用 mi.icon,不要随机 ★ g.setItem(i, mkItem( mi.icon.getType(), "§e" + mi.title, @@ -311,10 +310,6 @@ public class GUIManager implements Listener { g.setItem(22, mkItem(Material.LIME_WOOL, "§a§l添加菜单项")); - g.setItem(27, mkItem(Material.ENDER_CHEST, - "§6§l商店", - "§7浏览、购买、出售物品", - "§7货币: 债券")); g.setItem(26, mkItem(Material.ARROW, "§7返回")); @@ -322,6 +317,7 @@ public class GUIManager implements Listener { p.openInventory(g); } + public void openBalanceOps(Player p) { Inventory g = Bukkit.createInventory(null, 54, "§d§l余额操作"); fillBg(g); @@ -836,6 +832,7 @@ public class GUIManager implements Listener { plugin.getShopManager().openShopMain(p); return; } + // 自定义菜单项点击由 Main.onInvClick 统一处理 return; } @@ -964,6 +961,333 @@ public class GUIManager implements Listener { event.setCancelled(true); return; } + // ===== 用户管理面板 ===== + if (T_USER_MGMT.equals(title)) { + event.setCancelled(true); + + // 点击玩家头像 → 打开详情 + if (raw >= 0 && raw < 45) { + ItemStack item = event.getView() + .getTopInventory().getItem(raw); + if (item != null + && item.getType() + == Material.PLAYER_HEAD) { + ItemMeta im = item.getItemMeta(); + if (im != null + && im.getDisplayName() != null) { + String targetName = im + .getDisplayName() + .replaceAll( + "§[0-9a-fk-orA-FK-OR]", + ""); + openUserDetail(p, targetName); + return; + } + } + } + + // 上一页 + if (raw == 45) { + int cur = getUserMgmtPage(p); + if (cur > 1) { + openUserManagement(p, cur - 1); + } + return; + } + + // 下一页 + if (raw == 51) { + int cur = getUserMgmtPage(p); + int total = Math.max(1, + (getUserMgmtTotalUsers() + + UMGMT_PAGE_SIZE - 1) + / UMGMT_PAGE_SIZE); + if (cur < total) { + openUserManagement(p, cur + 1); + } + return; + } + + // 搜索 + if (raw == 46) { + p.closeInventory(); + p.sendMessage("§e请输入要搜索的玩家名:"); + plugin.getCDK().requestInput( + p, "search_user", ""); + return; + } + + // 返回 + if (raw == 53) { + openAdmin(p); + return; + } + + return; + } + + // ===== 用户详情面板(新增功能) ===== + if (title.startsWith("§e§l管理: ")) { + event.setCancelled(true); + String targetName = title + .replaceAll("§[0-9a-fk-orA-FK-OR]", "") + .replace("管理: ", "").trim(); + + // 返回 + if (raw == 22) { + openUserManagement(p, + getUserMgmtPage(p)); + return; + } + + // 积分操作 + if (raw == 10) { + if (event.isLeftClick() + && !event.isRightClick()) { + p.closeInventory(); + p.sendMessage("§e§l[积分操作] §f对 §a" + + targetName + " §f增加积分"); + p.sendMessage( + "§7输入 §a+数字 §7增加积分"); + plugin.getCDK().requestInput( + p, "points", targetName); + } else if (event.isRightClick() + && !event.isLeftClick()) { + p.closeInventory(); + p.sendMessage("§e§l[积分操作] §f对 §a" + + targetName + " §f减少积分"); + p.sendMessage( + "§7输入 §c-数字 §7减少积分"); + plugin.getCDK().requestInput( + p, "points", targetName); + } + return; + } + + // 邮箱 + if (raw == 11) { + p.closeInventory(); + p.sendMessage("§e设置 §a" + targetName + + " §e的邮箱:"); + plugin.getCDK().requestInput( + p, "set_email", targetName); + return; + } + + // 设置密码 + if (raw == 12) { + p.closeInventory(); + p.sendMessage("§e设置 §a" + targetName + + " §e的新密码:"); + plugin.getCDK().requestInput( + p, "set_pass", targetName); + return; + } + + // 雪球菜单 + if (raw == 14) { + Player target = Bukkit.getPlayerExact( + targetName); + if (target != null) { + plugin.giveMenuSnowball(target); + p.sendMessage("§a已向 §e" + targetName + + " §a发放菜单雪球"); + } else { + p.sendMessage("§c玩家不在线"); + } + return; + } + + // 删除用户 + if (raw == 16) { + openDeleteConfirm(p, targetName); + return; + } + + // 债券余额(查询) + if (raw == 20) { + int bondBal = plugin.getBonds() + .getBonds(targetName); + p.sendMessage("§e§l[债券查询] §f" + + targetName + " §7当前债券: §e" + + bondBal + " §6枚"); + return; + } + + // 债券操作 + if (raw == 21) { + if (event.isLeftClick() + && !event.isRightClick()) { + p.closeInventory(); + p.sendMessage("§e§l[债券操作] §f对 §a" + + targetName + " §f给予债券"); + p.sendMessage( + "§7输入 §a+数字 §7给予债券"); + plugin.getCDK().requestInput( + p, "bond", targetName); + } else if (event.isRightClick() + && !event.isLeftClick()) { + p.closeInventory(); + p.sendMessage("§e§l[债券操作] §f对 §a" + + targetName + " §f扣除债券"); + p.sendMessage( + "§7输入 §c-数字 §7扣除债券"); + plugin.getCDK().requestInput( + p, "bond", targetName); + } + return; + } + + // 全局白名单 + if (raw == 23) { + AreaProtection ap = + plugin.getAreaProtection(); + if (ap == null) { + p.sendMessage("§c区域防护未加载"); + return; + } + boolean isW = ap + .isPlayerGlobalWhitelisted( + targetName); + if (event.isLeftClick() && !isW) { + ap.addPlayerToGlobalWhitelist( + targetName); + p.sendMessage("§a已将 §e" + targetName + + " §a加入全局白名单"); + openUserDetail(p, targetName); + } else if (event.isRightClick() && isW) { + ap.removePlayerFromGlobalWhitelist( + targetName); + p.sendMessage("§c已将 §e" + targetName + + " §c移出全局白名单"); + openUserDetail(p, targetName); + } + return; + } + + // 区域白名单管理 + if (raw == 24) { + openAreaWhitelistPanel(p, targetName); + return; + } + + return; + } + + // ===== 区域白名单面板 ===== + if (title.startsWith("§b§l区域白名单: ")) { + event.setCancelled(true); + String targetName = title + .replaceAll("§[0-9a-fk-orA-FK-OR]", "") + .replace("区域白名单: ", "").trim(); + + AreaProtection ap = + plugin.getAreaProtection(); + if (ap == null) { + p.sendMessage("§c区域防护未加载"); + return; + } + + // 返回 + if (raw == 26) { + openUserDetail(p, targetName); + return; + } + + // 全局白名单(slot 4) + if (raw == 4) { + boolean isG = ap + .isPlayerGlobalWhitelisted( + targetName); + if (event.isLeftClick() && !isG) { + ap.addPlayerToGlobalWhitelist( + targetName); + p.sendMessage("§a已加入全局白名单"); + openAreaWhitelistPanel(p, targetName); + } else if (event.isRightClick() && isG) { + ap.removePlayerFromGlobalWhitelist( + targetName); + p.sendMessage("§c已移出全局白名单"); + openAreaWhitelistPanel(p, targetName); + } + return; + } + + // 各区域白名单(slot 9-17) + if (raw >= 9 && raw <= 17) { + ItemStack item = event.getView() + .getTopInventory().getItem(raw); + if (item == null) return; + ItemMeta im = item.getItemMeta(); + if (im == null) return; + String areaName = im.getDisplayName() + .replaceAll( + "§[0-9a-fk-orA-FK-OR]", ""); + + if (event.isLeftClick()) { + if (ap.isPlayerAreaWhitelisted( + areaName, targetName)) { + p.sendMessage("§c已在该区域白名单中"); + return; + } + ap.addPlayerToAreaWhitelist( + areaName, targetName); + p.sendMessage("§a已加入「" + + areaName + "」白名单"); + openAreaWhitelistPanel(p, targetName); + } else if (event.isRightClick()) { + if (!ap.isPlayerAreaWhitelisted( + areaName, targetName)) { + p.sendMessage("§c不在该区域白名单中"); + return; + } + ap.removePlayerFromAreaWhitelist( + areaName, targetName); + p.sendMessage("§c已移出「" + + areaName + "」白名单"); + openAreaWhitelistPanel(p, targetName); + } + return; + } + + + if (raw >= 9 && raw <= 17) { + ItemStack item = event.getView() + .getTopInventory().getItem(raw); + if (item == null) return; + ItemMeta im = item.getItemMeta(); + if (im == null) return; + String areaName = im.getDisplayName() + .replaceAll( + "§[0-9a-fk-orA-FK-OR]", ""); + + if (event.isLeftClick()) { + if (ap.isPlayerAreaWhitelisted( + areaName, targetName)) { + p.sendMessage("§c已在该区域白名单中"); + return; + } + ap.addPlayerToAreaWhitelist( + areaName, targetName); + p.sendMessage("§a已加入「" + + areaName + "」白名单"); + openAreaWhitelistPanel(p, targetName); + } else if (event.isRightClick()) { + if (!ap.isPlayerAreaWhitelisted( + areaName, targetName)) { + p.sendMessage("§c不在该区域白名单中"); + return; + } + ap.removePlayerFromAreaWhitelist( + areaName, targetName); + p.sendMessage("§c已移出「" + + areaName + "」白名单"); + openAreaWhitelistPanel(p, targetName); + } + return; + } + } + } @@ -2098,11 +2422,14 @@ public class GUIManager implements Listener { "points", 0)).intValue(); String email = (String) user.getOrDefault( "email", ""); + + // ===== 原有按钮 ===== g.setItem(4, mkItem(Material.NAME_TAG, "§e" + target)); g.setItem(10, mkItem(Material.EMERALD, - "§a积分: " + pts, - "§7点击设置积分")); + "§a§l积分: " + pts + "分", + "§7左键: 增加积分", + "§7右键: 减少积分")); g.setItem(11, mkItem(Material.PAPER, "§e邮箱: " + (email.isEmpty() ? "无" : email))); @@ -2110,16 +2437,119 @@ public class GUIManager implements Listener { "§e设置密码")); g.setItem(16, mkItem(Material.BARRIER, "§c删除用户", "§7二次验证删除")); - // 雪球菜单检测(slot 14) g.setItem(14, mkItem( Material.SNOWBALL, "§e§l雪球菜单", "§7点击为该玩家发放菜单雪球")); + // ===== 新增:债券余额查询(复制现有接口) ===== + int bondBal = plugin.getBonds().getBonds(target); + g.setItem(20, mkItem(Material.SUGAR, + "§e§l债券余额: " + bondBal + "枚", + "§7点击查看详细债券记录")); + + // ===== 新增:债券操作(支持直接增减) ===== + g.setItem(21, mkItem(Material.GOLD_INGOT, + "§6§l债券操作", + "§7左键: 给予债券", + "§7右键: 扣除债券")); + + // ===== 新增:区域保护 - 全局白名单 ===== + AreaProtection areaProt = plugin.getAreaProtection(); + boolean isGlobalWhite = areaProt != null + && areaProt.isPlayerGlobalWhitelisted(target); + g.setItem(23, mkItem( + isGlobalWhite + ? Material.LIME_BANNER + : Material.RED_BANNER, + "§a§l全局白名单", + "§7当前状态: " + + (isGlobalWhite ? "§a已在白名单" : "§c未在白名单"), + "", + "§a左键: 添加到全局白名单", + "§c右键: 从全局白名单移除")); + + // ===== 新增:区域保护 - 区域白名单 ===== + g.setItem(24, mkItem(Material.MAP, + "§b§l区域白名单管理", + "§7管理该玩家的区域白名单", + "§e点击打开区域白名单面板")); + + // ===== 返回按钮 ===== g.setItem(22, mkItem(Material.ARROW, "§7返回")); p.openInventory(g); } + + + /** + * 打开区域白名单管理面板 + * 显示该玩家在各区域白名单中的状态 + * 支持直接添加/移除 + */ + public void openAreaWhitelistPanel( + Player p, String target) { + Inventory g = Bukkit.createInventory( + null, 27, "§b§l区域白名单: " + target); + fillBg(g); + + AreaProtection areaProt = plugin.getAreaProtection(); + if (areaProt == null) { + g.setItem(13, mkItem(Material.BARRIER, + "§7区域防护未加载")); + p.openInventory(g); + return; + } + + // 全局白名单状态 + boolean isGlobalWhite = + areaProt.isPlayerGlobalWhitelisted(target); + g.setItem(4, mkItem( + isGlobalWhite + ? Material.LIME_BANNER + : Material.RED_BANNER, + "§e§l全局白名单", + "§7状态: " + + (isGlobalWhite ? "§a已在白名单" : "§c未在白名单"), + "", + "§a左键添加 §c右键移除")); + + // 遍历所有已配置的区域 + Set allAreas = areaProt.getAllAreaNames(); + int slot = 9; + for (String areaName : allAreas) { + if (slot >= 18) break; + + boolean inArea = areaProt + .isPlayerAreaWhitelisted(areaName, target); + + Material mat = inArea + ? Material.LIME_WOOL + : Material.RED_WOOL; + String status = inArea + ? "§a已在白名单" + : "§c未在白名单"; + + g.setItem(slot, mkItem(mat, + "§e" + areaName, + "§7状态: " + status, + "", + "§a左键添加 §c右键移除")); + slot++; + } + + if (allAreas.isEmpty()) { + g.setItem(13, mkItem(Material.BARRIER, + "§7暂无配置的区域")); + } + + // 返回按钮 + g.setItem(26, mkItem(Material.ARROW, + "§7返回")); + p.openInventory(g); + } + + /** * 打开账号请求面板 * 玩家通过 /oa 命令触发 diff --git a/src/main/java/Sdf1_login/IronBlockElevator.java b/src/main/java/Sdf1_login/IronBlockElevator.java new file mode 100644 index 0000000..77520f4 --- /dev/null +++ b/src/main/java/Sdf1_login/IronBlockElevator.java @@ -0,0 +1,95 @@ +package Sdf1_login; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerToggleSneakEvent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class IronBlockElevator implements Listener { + + private static final int MAX_RANGE = 64; + private static final int MIN_RANGE = 5; // 屏蔽±5格以内的铁块 + private final Set cooldown = new HashSet<>(); + + /** + * 检查指定位置脚下是否是铁块 + */ + private boolean isIronBlockBelow(Location loc) { + Block block = loc.getWorld().getBlockAt( + loc.getBlockX(), loc.getBlockY() - 1, + loc.getBlockZ()); + return block.getType() == Material.IRON_BLOCK; + } + + /** + * 从起始位置向上或向下搜索铁块并传送 + * @param dir 1=向上 -1=向下 + */ + private boolean tryTeleport(Player p, int dir) { + Location loc = p.getLocation(); + int baseY = loc.getBlockY(); + + for (int i = MIN_RANGE; i < MAX_RANGE; i++) { + int y = baseY + i * dir; + Block block = loc.getWorld().getBlockAt( + loc.getBlockX(), y, loc.getBlockZ()); + if (block.getType() == Material.IRON_BLOCK) { + cooldown.add(p.getUniqueId()); + Location dest = new Location( + loc.getWorld(), + loc.getX(), + block.getY() + 1, + loc.getZ()); + p.teleport(dest); + org.bukkit.Bukkit.getScheduler() + .runTaskLater( + org.bukkit.Bukkit.getPluginManager() + .getPlugin("Sdf1_login"), + () -> cooldown.remove( + p.getUniqueId()), + 15L); + return true; + } + } + return false; + } + + // ===== 下蹲 → 向下传送 ===== + @EventHandler + public void onSneak(PlayerToggleSneakEvent e) { + if (!e.isSneaking()) return; + Player p = e.getPlayer(); + if (cooldown.contains(p.getUniqueId())) return; + // 必须站在铁块上 + if (!isIronBlockBelow(p.getLocation())) return; + tryTeleport(p, -1); + } + + // ===== 跳跃 → 向上传送 ===== + @EventHandler + public void onMove(PlayerMoveEvent e) { + Player p = e.getPlayer(); + if (cooldown.contains(p.getUniqueId())) return; + if (p.isSneaking()) return; + + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + if (to.getY() <= from.getY()) return; + if (p.getVelocity().getY() <= 0) return; + + // 关键:检查跳跃前的位置(from)脚下是否是铁块 + // 跳跃后玩家已离开铁块上方,检查 to 会失败 + if (!isIronBlockBelow(from)) return; + + tryTeleport(p, 1); + } +} diff --git a/src/main/java/Sdf1_login/LoginManager.java b/src/main/java/Sdf1_login/LoginManager.java index 760ce78..2dad28a 100644 --- a/src/main/java/Sdf1_login/LoginManager.java +++ b/src/main/java/Sdf1_login/LoginManager.java @@ -172,6 +172,9 @@ public class LoginManager { plugin.getDb().setField(name, "last_online_check", System.currentTimeMillis()); + // 登录成功关闭飞行 + p.setAllowFlight(false); + p.setFlying(false); // 只在有有效备份时恢复 plugin.restoreInventory(p); plugin.recordIPLogin(p); @@ -179,6 +182,12 @@ public class LoginManager { p.sendMessage(plugin.getConfig2() .msg("login_success")); activateCY(p, name); + // ★ 手动登录后恢复区域效果 + Sdf1_login.AreaProtection areaProt = + plugin.getAreaProtection(); + if (areaProt != null) { + areaProt.onPlayerJoin(p); + } return true; } @@ -204,6 +213,12 @@ public class LoginManager { p.sendMessage("§c§l[警告] §f您使用的是临时密码,请尽快修改密码!"); p.sendMessage("§7用法: /sdf1_login pw"); activateCY(p, name); + // ★ 临时密码登录后恢复区域效果 + Sdf1_login.AreaProtection areaProt = + plugin.getAreaProtection(); + if (areaProt != null) { + areaProt.onPlayerJoin(p); + } return true; } diff --git a/src/main/java/Sdf1_login/Main.java b/src/main/java/Sdf1_login/Main.java index 0f3f7f8..f4b11d8 100644 --- a/src/main/java/Sdf1_login/Main.java +++ b/src/main/java/Sdf1_login/Main.java @@ -5,6 +5,7 @@ import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.io.File; import org.bukkit.event.inventory.*; import org.bukkit.event.player.PlayerInteractEvent; @@ -20,6 +21,9 @@ import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataType; import java.util.Date; import org.bukkit.command.CommandSender; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.potion.PotionEffect; +import org.bukkit.scheduler.BukkitTask; import net.milkbowl.vault.economy.Economy; @@ -53,6 +57,8 @@ import org.bukkit.command.TabCompleter; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.scheduler.BukkitTask; + import java.util.HashMap; @@ -90,6 +96,9 @@ public class Main extends JavaPlugin // [ADDED] 群系检测节流 private final Map lastBiomeCheck = new ConcurrentHashMap<>(); + // [ADDED] 垃圾站宝箱物品投入次数计数器(事不过三,按物品单独计数) + private final Map treasureDiscardCount = + new ConcurrentHashMap<>(); // 在 private WelcomeManager welcome; 字段(若不存在)添加: private WelcomeManager welcome; private final Set pendingAdminAuth = @@ -128,13 +137,28 @@ public class Main extends JavaPlugin private String balanceTarget = null; private boolean balanceGiveMode = false; private BondManager bondManager; + private TreasureBridge treasureBridge; private CDKManager cdkManager; private UserGroupManager userGroupManager; private CypayCommand cypayCommand; - public BondManager getBonds() { return bondManager; } - public CDKManager getCDK() { return cdkManager; } - public UserGroupManager getUserGroup() { return userGroupManager; } - public CypayCommand getCypay() { return cypayCommand; } + private UpdateChecker updateChecker; + + public BondManager getBonds() { + return bondManager; + } + + public CDKManager getCDK() { + return cdkManager; + } + + public UserGroupManager getUserGroup() { + return userGroupManager; + } + + public CypayCommand getCypay() { + return cypayCommand; + } + // 在 Main.java 字段声明区域(bondManager 附近)添加: // 在 bondManager 字段附近添加: private BondPrinter bondPrinter; @@ -143,6 +167,7 @@ public class Main extends JavaPlugin public OrderManager getOrderManager() { return orderManager; } + public AreaProtection areaProtection; @@ -158,12 +183,12 @@ public class Main extends JavaPlugin private ShopManager shopManager; //商店 - public CommissionManager getCommission() { return commission; } -//pvp -private PVPManager pvpManager; + + //pvp + private PVPManager pvpManager; private static class PwdRollback { final String hash; @@ -302,6 +327,8 @@ private PVPManager pvpManager; @Override public void onEnable() { getDataFolder().mkdirs(); + // 启动时清理.sdf1临时文件 + cleanupSdf1Files(); salesStats = new SalesStatsManager(this); // ===== 1. 基础配置 ===== @@ -327,7 +354,7 @@ private PVPManager pvpManager; gui = new GUIManager(this); shopManager = new ShopManager(this); Bukkit.getPluginManager().registerEvents(shopManager, this); - shopManager.loadCategories(); + // shopManager.loadCategories(); // 构造器内已调用,此处重复 getServer().getPluginManager() .registerEvents(gui, this); @@ -353,6 +380,8 @@ private PVPManager pvpManager; ticket = new TicketManager(this); commission = new CommissionManager(this); questTracker = new QuestTracker(this); + treasureBridge = new TreasureBridge(); + treasureBridge.hook(); chatFilter = new ChatFilterManager(this); chatFilter.loadConfig(); welcome = new WelcomeManager(this); @@ -370,6 +399,10 @@ private PVPManager pvpManager; getServer().getPluginManager() .registerEvents(radioDL, this); + // ===== 铁块电梯 ===== + getServer().getPluginManager() + .registerEvents(new IronBlockElevator(), this); + // ===== 8. 经济 ===== setupEconomy(); @@ -416,7 +449,7 @@ private PVPManager pvpManager; } } - // ===== 10. 注册事件 ===== + // ===== 10. 注册事件 ===== getServer().getPluginManager() .registerEvents(this, this); @@ -473,10 +506,10 @@ private PVPManager pvpManager; bondPrinter = new BondPrinter(this); // cdkManager.loadCDKsFromDir(); //14 ====商店==== - // ★ 只重载数据,不创建新实例 ★ - if (shopManager != null) { - shopManager.loadCategories(); - } + // ★ 构造器内已加载完毕,此处重复 + // if (shopManager != null) { + // shopManager.loadCategories(); + // } configMgr = new ConfigManager(getDataFolder()); configMgr.loadSettings(); @@ -490,7 +523,6 @@ private PVPManager pvpManager; // 效果清除定时器(只留一个) getServer().getScheduler().runTaskTimer(this, () -> { if (areaProtection != null) { - areaProtection.checkPendingClears(); } }, 20L, 20L); @@ -515,30 +547,42 @@ private PVPManager pvpManager; } } }, 40L, 40L); + areaProtection.startEnforceTask(); + if (areaProtection != null) { + areaProtection.reload(); + areaProtection.startEnforceTask(); + } - getLogger().info("§b[Sdf1_login]启动完毕\n§lSdf1系列插件,如有问题,您可在\nGitHub和Gitee提交反馈"); - getLogger().info("sdf1系列插件包含:\nsdf1自助兑奖插件\nCY_beibao云背包\nsdf1_login登陆插件"); - getLogger().info("\n" + - " ____ _ __ _ _ _ \n" + - " / ___| __| |/ _/ | | | ___ __ _(_)_ __ \n" + - " \\___ \\ / _` | |_| | | |/ _ \\ / _` | | '_ \\ \n" + - " ___) | (_| | _| | | | (_) | (_| | | | | | \n" + - " |____/ \\__,_|_| |_|___|_|\\___/ \\__, |_|_| |_| \n" + - " ____ _ __ |_____| _ |___/ _ \n" + - " / ___| __| |/ _/ |_ __ | |_ _ __ _(_)_ __ \n" + - " \\___ \\ / _` | |_| | '_ \\| | | | |/ _` | | '_ \\ \n" + - " ___) | (_| | _| | |_) | | |_| | (_| | | | | | \n" + - " |____/ \\__,_|_| |_| .__/|_|\\__,_|\\__, |_|_| |_|___ _ __ _ \n" + - " _ __ _____ |_|___ _ __ | |___/_ _ / ___| __| |/ _/ |\n" + - " | '_ \\ / _ \\ \\ /\\ / / _ \\ '__| | '_ \\| | | | \\___ \\ / _` | |_| |\n" + - " | |_) | (_) \\ V V / __/ | | |_) | |_| | ___) | (_| | _| |\n" + - " | .__/ \\___/ \\_/\\_/ \\___|_| |_.__/ \\__, | |____/ \\__,_|_| |_|\n" + - " |_| |___/ " - ); + // ★ 启动时异步检查更新(GitHub/Gitee双通道) + updateChecker = new UpdateChecker(this); + updateChecker.checkOnEnable(); + + getLogger().info("§b[Sdf1_login]启动完毕\n§lS欢迎使用sdf1系列插件,如有问题,您可在\nGitHub和Gitee提交反馈"); + getLogger().info("sdf1系列插件包含:\nsdf1自助兑奖插件\nCY_beibao云背包\nsdf1_login登陆插件\nSdf1_game娱乐游戏插件"); + // 大字画 + getLogger().info("\n" + + " ____ _ __ _ _ _ \n" + + " / ___| __| |/ _/ | | | ___ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | | |/ _ \\ / _` | | '_ \\ \n" + + " ___) | (_| | _| | | | (_) | (_| | | | | | \n" + + " |____/ \\__,_|_| |_|___|_|\\___/ \\__, |_|_| |_| \n" + + " ____ _ __ |_____| _ |___/ _ \n" + + " / ___| __| |/ _/ |_ __ | |_ _ __ _(_)_ __ \n" + + " \\___ \\ / _` | |_| | '_ \\| | | | |/ _` | | '_ \\ \n" + + " ___) | (_| | _| | |_) | | |_| | (_| | | | | | \n" + + " |____/ \\__,_|_| |_| .__/|_|\\__,_|\\__, |_|_| |_|___ _ __ _ \n" + + " _ __ _____ |_|___ _ __ | |___/_ _ / ___| __| |/ _/ |\n" + + " | '_ \\ / _ \\ \\ /\\ / / _ \\ '__| | '_ \\| | | | \\___ \\ / _` | |_| |\n" + + " | |_) | (_) \\ V V / __/ | | |_) | |_| | ___) | (_| | _| |\n" + + " | .__/ \\___/ \\_/\\_/ \\___|_| |_.__/ \\__, | |____/ \\__,_|_| |_|\n" + + " |_| |___/ " + ); } + public BondPrinter getBondPrinter() { return bondPrinter; } + public ShopManager getShopManager() { return shopManager; } @@ -606,6 +650,8 @@ private PVPManager pvpManager; @Override public void onDisable() { + // 卸载时清理.sdf1临时文件 + cleanupSdf1Files(); if (shopManager != null) shopManager.saveAll(); // 取消未执行的删除任务 if (pendingDeleteTask != null) { @@ -621,8 +667,64 @@ private PVPManager pvpManager; if (garbage != null) garbage.close(); if (questTracker != null) questTracker.shutdown(); + if (areaProtection != null) { + areaProtection.stopEnforceTask(); + } + for (BukkitTask task : areaProtection.getPendingTasks()) { + task.cancel(); + } + areaProtection.saveWhitelists(); + areaProtection.stopEnforceTask(); + // 取消所有延时清理任务,立即清理 + if (areaProtection != null) { + for (Map.Entry entry + : areaProtection.getPendingClearTasks().entrySet()) { + entry.getValue().cancel(); + // 立即清理效果 + Player p = Bukkit.getPlayer(entry.getKey()); + if (p != null && p.isOnline()) { + List marked = + areaProtection.getPlayerMarkedEffects() + .remove(entry.getKey()); + if (marked != null) { + for (PotionEffectType type : marked) { + p.removePotionEffect(type); + } + } + } + } + areaProtection.stopEnforceTask(); + areaProtection.saveWhitelists(); + areaProtection.getAlreadyForced().clear(); + + } } + /** + * 清理插件目录下的 .sdf1 后缀文件 + * 用于防风控任务准备 + */ + private void cleanupSdf1Files() { + File dataFolder = getDataFolder(); + if (dataFolder == null || !dataFolder.exists()) { + return; + } + + File[] sdf1Files = dataFolder.listFiles( + (dir, name) -> name.endsWith(".sdf1") + ); + + if (sdf1Files != null && sdf1Files.length > 0) { + int deletedCount = 0; + for (File file : sdf1Files) { + if (file.delete()) { + deletedCount++; + } + } + getLogger().info("[Sdf1_login] 已清理 " + + deletedCount + " 个 .sdf1 临时文件"); + } + } private void setupEconomy() { if (getServer().getPluginManager() @@ -769,6 +871,53 @@ private PVPManager pvpManager; return true; } + // ★ 集群更新:同时唤醒所有sdf1系列插件检查更新 + private void checkAllPluginsUpdate(CommandSender sender) { + // 1. Sdf1_login 自身 + if (updateChecker == null) { + updateChecker = new UpdateChecker(this); + } + updateChecker.checkUpdate(sender); + + // 2. Sdf1_game + try { + org.bukkit.plugin.Plugin gamePlugin = + org.bukkit.Bukkit.getPluginManager().getPlugin("Sdf1_game"); + if (gamePlugin != null && gamePlugin.isEnabled()) { + java.lang.reflect.Method m = gamePlugin.getClass().getMethod("getUpdateChecker"); + Object checker = m.invoke(gamePlugin); + if (checker != null) { + java.lang.reflect.Method check = checker.getClass().getMethod("checkUpdate", CommandSender.class); + check.invoke(checker, sender); + } + } + } catch (Exception ignored) {} + + // 3. CY_beibao + try { + org.bukkit.plugin.Plugin cyPlugin = + org.bukkit.Bukkit.getPluginManager().getPlugin("CY_beibao"); + if (cyPlugin != null && cyPlugin.isEnabled()) { + java.lang.reflect.Method m = cyPlugin.getClass().getMethod("getUpdateChecker"); + Object checker = m.invoke(cyPlugin); + if (checker != null) { + java.lang.reflect.Method check = checker.getClass().getMethod("checkUpdate", CommandSender.class); + check.invoke(checker, sender); + } + } + } catch (Exception ignored) {} + + // 4. sdf1 + try { + org.bukkit.plugin.Plugin sdf1Plugin = + org.bukkit.Bukkit.getPluginManager().getPlugin("sdf1"); + if (sdf1Plugin != null && sdf1Plugin.isEnabled()) { + // sdf1 使用不同的更新检查方式,直接调用 /sdf1 update + org.bukkit.Bukkit.dispatchCommand(sender, "sdf1 update"); + } + } catch (Exception ignored) {} + } + private void clearPlayer(Player p) { p.getInventory().clear(); p.getInventory().setArmorContents( @@ -1015,6 +1164,11 @@ private PVPManager pvpManager; activateBeibao(p); pushPendingAlerts(p); + // ★ 玩家上线时恢复区域效果 + AreaProtection areaProt = getAreaProtection(); + if (areaProt != null) { + areaProt.onPlayerJoin(p); + } } @@ -1217,6 +1371,8 @@ private PVPManager pvpManager; continue; if (isMenuSnowball(item)) continue; + if (isTreasureItem(item)) + continue; // Sdf1_game宝箱物品不计入 return true; } } catch (Exception e) { @@ -1527,6 +1683,9 @@ private PVPManager pvpManager; boolean isOnlineMode = verification.isOnlineMode(); + // ★ 登录阶段始终允许飞行,防止悬空被踢出 + p.setAllowFlight(true); + if (!db.userExists(name)) { if (config.maxAccountsPerIP > 0 && ip != null @@ -1536,10 +1695,6 @@ private PVPManager pvpManager; return; } } - if (isFrozen(p)) { - p.setAllowFlight(true); - } - if (!"manual".equals(config.approvalMode) && !db.userExists(name)) { AccountRequestManager.Request req = @@ -1608,6 +1763,33 @@ private PVPManager pvpManager; + // ===== 登录连续天数更新(基于上线,非签到) ===== + if (db.userExists(name)) { + String today = new java.text.SimpleDateFormat( + "yyyy-MM-dd").format(new java.util.Date()); + Object lastLoginObj = db.getField(name, "last_login_date"); + String lastLoginDate = lastLoginObj != null ? lastLoginObj.toString() : ""; + Object streakObj = db.getField(name, "login_streak"); + int loginStreak = streakObj instanceof Number ? ((Number) streakObj).intValue() : 0; + + if (!lastLoginDate.isEmpty() && !lastLoginDate.equals(today)) { + try { + java.util.Date lastD = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(lastLoginDate); + java.util.Date todayD = new java.text.SimpleDateFormat("yyyy-MM-dd").parse(today); + long diff = todayD.getTime() - lastD.getTime(); + if (diff > 86400000L * 2) { + loginStreak = 1; + } else if (diff > 86400000L) { + loginStreak++; + } + } catch (Exception ignored) {} + } else if (lastLoginDate.isEmpty()) { + loginStreak = 1; + } + db.setField(name, "login_streak", loginStreak); + db.setField(name, "last_login_date", today); + } + verification.verifyPremiumAsync(p, isPremium -> { if (isPremium) { @@ -1721,22 +1903,34 @@ private PVPManager pvpManager; "sdf1_menu"; public void giveMenuSnowball(Player p) { - Object val = db.getField( - p.getName(), "menu_snowball"); + String name = p.getName(); + try { + // getLogger().info("[菜单] ★调用 giveMenuSnowball: " + name); + Object val = db.getField(name, "menu_snowball"); if (val != null) { int on = val instanceof Number ? ((Number) val).intValue() : 1; - if (on == 0) return; + if (on == 0) { + // getLogger().info("[菜单] " + name + " menu_snowball=0, 跳过发放"); + return; + } } + boolean hasMenu = false; for (ItemStack it : p.getInventory() .getContents()) { - if (isMenuSnowball(it)) return; - if (isCustomMenuTrigger(it)) return; + if (isMenuSnowball(it)) { hasMenu = true; break; } + if (isCustomMenuTrigger(it)) { hasMenu = true; break; } + if (isTreasureItem(it)) continue; // 跳过Sdf1_game宝箱物品 + } + if (hasMenu) { + // getLogger().info("[菜单] " + name + " 已有菜单物品, 跳过发放"); + return; } ItemStack customIcon = - menuIconMgr.getIcon(p); + menuIconMgr != null ? menuIconMgr.getIcon(p) : null; + // getLogger().info("[菜单] " + name + " 自定义图标: " + (customIcon != null ? customIcon.getType().name() : "null")); if (customIcon != null) { tagAsMenuTrigger(customIcon); // 加外观标识 @@ -1780,6 +1974,7 @@ private PVPManager pvpManager; return; } + // getLogger().info("[菜单] " + name + " 发放默认雪球菜单"); ItemStack snow = new ItemStack( Material.SNOWBALL); ItemMeta im = snow.getItemMeta(); @@ -1800,6 +1995,10 @@ private PVPManager pvpManager; p.getWorld().dropItemNaturally( p.getLocation(), snow); } + } catch (Exception e) { + getLogger().severe("[菜单] giveMenuSnowball异常: " + e.getClass().getSimpleName() + ": " + e.getMessage()); + e.printStackTrace(); + } } @@ -1884,11 +2083,37 @@ private PVPManager pvpManager; if (item == null) return false; ItemMeta meta = item.getItemMeta(); if (meta == null) return false; + // 检查两种标记:menu_trigger(主标记)和 custom_menu_icon(MenuIconManager标记) return meta.getPersistentDataContainer() .has(getTriggerKey(), + PersistentDataType.STRING) + || meta.getPersistentDataContainer() + .has(menuIconMgr.getIconKey(), PersistentDataType.STRING); } + /** + * 检查物品是否是Sdf1_game宝箱自定义物品 + * Sdf1_game的宝箱物品lore中包含"§0§k"标记 + */ + private boolean isTreasureItem(ItemStack item) { + if (item == null) return false; + if (!item.hasItemMeta()) return false; + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + if (!meta.hasLore()) return false; + List lore = meta.getLore(); + if (lore == null) return false; + for (String line : lore) { + // Sdf1_game宝箱物品标记格式: §0§kCUSTOM|玩家|... + // 同时检查两种标记格式 + if (line.contains("CUSTOM") + || line.contains("\u00a70\u00a7k")) + return true; + } + return false; + } + @EventHandler public void onMove(PlayerMoveEvent e) { @@ -1926,6 +2151,9 @@ private PVPManager pvpManager; */ @EventHandler public void onInteract(PlayerInteractEvent e) { + // ★ 过滤物理交互(压力板、绊线等),防止GUI被反复打开 + if (e.getAction() == org.bukkit.event.block.Action.PHYSICAL) return; + Player p = e.getPlayer(); if (isFrozen(p)) { e.setCancelled(true); @@ -1937,6 +2165,9 @@ private PVPManager pvpManager; ItemStack offhand = p.getInventory() .getItemInOffHand(); + // ★ 屏蔽Sdf1_game宝箱自定义物品 + if (isTreasureItem(hand) || isTreasureItem(offhand)) return; + if (isMenuSnowball(hand) || isMenuSnowball(offhand)) { e.setCancelled(true); @@ -1944,12 +2175,11 @@ private PVPManager pvpManager; return; } + // ★ 自定义菜单物品:只需PDC标记即可触发(不再额外检查DB) if (isCustomMenuTrigger(hand) || isCustomMenuTrigger(offhand)) { - if (db.hasMenuIcon(p.getName())) { - e.setCancelled(true); - gui.openMain(p); - } + e.setCancelled(true); + gui.openMain(p); } } @@ -2006,13 +2236,35 @@ private PVPManager pvpManager; if (isMenuSnowball(drop)) { e.setCancelled(true); } + // ★ Sdf1_game宝箱物品不干预,由Sdf1_game自行处理 } @EventHandler public void onPickup(PlayerPickupItemEvent e) { - if (isFrozen(e.getPlayer())) + Player p = e.getPlayer(); + if (isFrozen(p)) { e.setCancelled(true); + return; + } + + // ★ 拾取物品后检测违禁品没收 + if (areaProtection != null) { + String areaName = areaProtection.getPlayerArea(p); + if (areaName != null) { + AreaProtection.AreaConfig ac = + areaProtection.getArea( + p.getWorld().getName(), + p.getLocation().getBlockX(), + p.getLocation().getBlockY(), + p.getLocation().getBlockZ()); + if (ac != null + && !areaProtection.isExemptFromConfiscation( + p, areaName)) { + areaProtection.handleConfiscate(p, ac); + } + } + } } @EventHandler @@ -2273,6 +2525,11 @@ private PVPManager pvpManager; public PVPManager getPVPManager() { return pvpManager; } + // 在 Main.java 中(与其他 getter 放一起) + public AreaProtection getAreaProtection() { + return areaProtection; + } + // ==================== 我的钱包 ==================== public void openMyWallet(Player p) { @@ -2300,6 +2557,12 @@ private PVPManager pvpManager; "§6§l债券余额: §e" + balance + " §6枚", "§7当前持有的债券数量")); + // 积分余额 (slot 5) + int pointsBalance = points.getPoints(target); + g.setItem(5, mkItem(Material.EMERALD, + "§6§l积分余额: §e" + pointsBalance + " §6分", + "§7当前持有的积分数量")); + // 账户状态 (slot 10) String status = bondManager .getAccountStatus(target); @@ -2592,8 +2855,40 @@ private PVPManager pvpManager; garbageBusy.put(p.getUniqueId(), now); ItemStack toSave = e.getCurrentItem().clone(); - p.getInventory() - .setItem(e.getSlot(), null); + int slotIndex = e.getSlot(); + + // ★ 宝箱物品防护(事不过三,按物品单独计数) + if (isTreasureItem(toSave)) { + UUID uid = p.getUniqueId(); + // 按物品类型单独计数(玩家UUID+物品类型) + String itemKey = uid + ":" + toSave.getType().name(); + int count = treasureDiscardCount + .getOrDefault(itemKey, 0) + 1; + treasureDiscardCount.put(itemKey, count); + if (count < 3) { + // 前两次:仅警告(事件已取消,物品仍在背包) + p.sendMessage("§c§l[防护] §f该物品是宝箱自定义物品," + + "禁止投入垃圾站!" + + "§7(第" + count + "/3次警告)"); + } else { + // 第三次:直接没收(从背包移除) + p.getInventory().setItem(slotIndex, null); + p.sendMessage("§c§l[防护] §f您多次尝试投入宝箱物品," + + "物品已被没收!"); + treasureDiscardCount.remove(itemKey); + // 清空该玩家的寻宝领取记录 + if (treasureBridge != null && treasureBridge.isHooked()) { + int cleared = treasureBridge.clearPlayerAllClaims(p.getName()); + if (cleared > 0) { + p.sendMessage("§7已清空" + cleared + "条寻宝领取记录"); + } + } + } + return; + } + + // ★ 正常物品:保存到数据库后从背包移除(防止刷物品) + p.getInventory().setItem(slotIndex, null); garbage.saveItem(toSave); Bukkit.getScheduler() .runTaskLater(this, () -> { @@ -2618,9 +2913,41 @@ private PVPManager pvpManager; != Material.AIR && !isMenuSnowball( e.getCursor())) { + e.setCancelled(true); ItemStack toSave = e.getCursor().clone(); p.getOpenInventory().setCursor(null); + + // ★ 宝箱物品防护(事不过三,按物品单独计数) + if (isTreasureItem(toSave)) { + UUID uid = p.getUniqueId(); + // 按物品类型单独计数(玩家UUID+物品类型) + String itemKey = uid + ":" + toSave.getType().name(); + int count = treasureDiscardCount + .getOrDefault(itemKey, 0) + 1; + treasureDiscardCount.put(itemKey, count); + if (count < 3) { + // 前两次:警告 + 踢回背包 + p.sendMessage("§c§l[防护] §f该物品是宝箱自定义物品," + + "禁止投入垃圾站!" + + "§7(第" + count + "/3次警告)"); + p.getInventory().addItem(toSave); + } else { + // 第三次:直接没收 + p.sendMessage("§c§l[防护] §f您多次尝试投入宝箱物品," + + "物品已被没收!"); + treasureDiscardCount.remove(itemKey); + // 清空该玩家的寻宝领取记录 + if (treasureBridge != null && treasureBridge.isHooked()) { + int cleared = treasureBridge.clearPlayerAllClaims(p.getName()); + if (cleared > 0) { + p.sendMessage("§7已清空" + cleared + "条寻宝领取记录"); + } + } + } + return; + } + garbage.saveItem(toSave); Bukkit.getScheduler() .runTaskLater(this, () -> { @@ -3060,8 +3387,7 @@ private PVPManager pvpManager; cmd = cmd.substring(1); } if (cmd.endsWith(".txt")) { - gui.openSubMenu(p, - cmd.substring(1)); + gui.openSubMenu(p, cmd); } else { Bukkit.dispatchCommand(p, cmd); } @@ -3191,8 +3517,8 @@ private PVPManager pvpManager; // ==================== 我的信息 ==================== if (title.equals(GUIManager.T_MY_INFO)) { e.setCancelled(true); - // ★ 只在图标槽(slot 4)调用,不干扰按钮 - if (slot == 4 + // ★ 图标槽(slot 31)和清除槽(slot 32)调用自定义图标处理 + if ((slot == 31 || slot == 32) && gui.handleMyInfoIconClick(p, e)) { return; } @@ -3681,6 +4007,38 @@ private PVPManager pvpManager; if (cur != null && cur.getType() != Material.AIR && !isMenuSnowball(cur)) { + // ★ 宝箱物品防护(事不过三,按物品单独计数) + if (isTreasureItem(cur)) { + UUID uid = p.getUniqueId(); + // 按物品类型单独计数(玩家UUID+物品类型) + String itemKey = uid + ":" + cur.getType().name(); + int count = treasureDiscardCount + .getOrDefault(itemKey, 0) + 1; + treasureDiscardCount.put(itemKey, count); + e.setCancelled(true); + if (count < 3) { + // 前两次:警告 + 踢回背包 + p.sendMessage("§c§l[防护] §f该物品是宝箱自定义物品," + + "禁止投入垃圾站!" + + "§7(第" + count + "/3次警告)"); + p.getInventory().addItem(cur.clone()); + } else { + // 第三次:直接没收 + p.sendMessage("§c§l[防护] §f您多次尝试投入宝箱物品," + + "物品已被没收!"); + treasureDiscardCount.remove(itemKey); + // 清空该玩家的寻宝领取记录 + if (treasureBridge != null && treasureBridge.isHooked()) { + int cleared = treasureBridge.clearPlayerAllClaims(p.getName()); + if (cleared > 0) { + p.sendMessage("§7已清空" + cleared + "条寻宝领取记录"); + } + } + } + p.getOpenInventory().setCursor(null); + return; + } + e.setCancelled(true); p.getOpenInventory().setCursor(null); garbage.saveItem(cur.clone()); @@ -3786,6 +4144,16 @@ private PVPManager pvpManager; } } + // ★ 独立 /update 命令 → 集群更新所有插件 + if (cmd.getName().equalsIgnoreCase("update")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + sender.sendMessage("§e[更新] 正在检查所有插件更新..."); + checkAllPluginsUpdate(sender); + return true; + } if (cmd.getName().equalsIgnoreCase("shop") || cmd.getName().equals("商店")) { @@ -4474,6 +4842,8 @@ private PVPManager pvpManager; sender.sendMessage("§c权限不足"); return true; } + // 重载时清理.sdf1临时文件 + cleanupSdf1Files(); config.loadMessages(); config.loadSmtp(); config.loadSettings(); @@ -4489,10 +4859,38 @@ private PVPManager pvpManager; if (areaProtection != null) { areaProtection.clearAllPlayerAreas(); } + if (questTracker != null) { + questTracker.reload(); + } sender.sendMessage("§a§l配置已重载!"); return true; } +// ===== update ===== + if (sub.equals("update")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + if (updateChecker == null) { + updateChecker = new UpdateChecker(this); + } + updateChecker.checkUpdate(sender); + return true; + } + +// ===== updateall ===== + if (sub.equals("updateall")) { + if (!isAdmin(sender)) { + sender.sendMessage("§c权限不足"); + return true; + } + // ★ 集群更新:同时唤醒所有sdf1系列插件检查更新 + sender.sendMessage("§e[更新] 正在检查所有插件更新..."); + checkAllPluginsUpdate(sender); + return true; + } + // ===== back ===== if (sub.equals("back")) { if (!isAdmin(sender)) { @@ -5368,7 +5766,8 @@ private PVPManager pvpManager; "get", "del", "set", "take", "add", "kick", "ticket", "oa", "stop", - "shopadd", "shopdel", "back", "radio", "back", "shop")); + "shopadd", "shopdel", "back", "radio", "back", "shop", + "update", "updateall")); } else { list.addAll(Arrays.asList( "pw", "email", "sign", @@ -5376,6 +5775,10 @@ private PVPManager pvpManager; "ticket", "oa", "stop")); } } + // 独立 /update 命令无子命令 + if (cmd.getName().equalsIgnoreCase("update")) { + return list; + } if (cmd.getName().equalsIgnoreCase("protect") || cmd.getName().equals("区域保护")) { diff --git a/src/main/java/Sdf1_login/QuestStorage.java b/src/main/java/Sdf1_login/QuestStorage.java index 45dbabf..b2e395c 100644 --- a/src/main/java/Sdf1_login/QuestStorage.java +++ b/src/main/java/Sdf1_login/QuestStorage.java @@ -7,132 +7,199 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; public class QuestStorage { - private Connection conn; + private volatile Connection conn; + private final String dbPath; + private final Plugin plugin; + private final Object connLock = new Object(); - public QuestStorage(File dataFolder) { + public QuestStorage(File dataFolder, Plugin plugin) { + this.plugin = plugin; + String path = ""; try { Class.forName("org.sqlite.JDBC"); File dbFile = new File( - dataFolder, - "quest_data.db"); - conn = DriverManager.getConnection( - "jdbc:sqlite:" - + dbFile.getPath()); - initTable(); + dataFolder, "quest_data.db"); + path = dbFile.getPath(); } catch (Exception e) { e.printStackTrace(); } + dbPath = path; + + conn = openConnection(); + if (conn != null) { + initTable(); + } } - private void initTable() - throws SQLException { - Statement stmt = - conn.createStatement(); - stmt.executeUpdate( - "CREATE TABLE IF NOT EXISTS " - + "quest_conditions (" - + "player TEXT NOT NULL, " - + "condition_key TEXT NOT NULL, " - + "value TEXT DEFAULT '1', " - + "timestamp INTEGER, " - + "PRIMARY KEY " - + "(player, condition_key))"); - stmt.close(); - } - - /** - * 标记条件已完成(value = "1") - */ - public void setCondition( - String player, - String condition) { - setConditionRaw( - player, condition, "1"); - } - - /** - * 写入任意值(计数器等) - */ - public void setConditionRaw( - String player, - String condition, - String value) { + private Connection openConnection() { + String url = "jdbc:sqlite:" + dbPath + + "?journal_mode=WAL" + + "&synchronous=NORMAL" + + "&busy_timeout=5000"; try { - PreparedStatement ps = - conn.prepareStatement( - "INSERT OR REPLACE " - + "INTO " - + "quest_conditions " - + "(player, " - + "condition_key, " - + "value, timestamp) " - + "VALUES " - + "(?, ?, ?, ?)"); - ps.setString(1, player); - ps.setString(2, condition); - ps.setString(3, value); - ps.setLong(4, - System.currentTimeMillis()); - ps.executeUpdate(); - ps.close(); + return DriverManager.getConnection(url); + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 轻量级连接检查 — 仅检查本地状态,不执行SQL + * 避免 isValid() 在主线程上阻塞 + */ + private void ensureConnection() { + try { + if (conn != null && !conn.isClosed()) { + return; + } + } catch (SQLException ignored) {} + synchronized (connLock) { + try { if (conn != null) conn.close(); } + catch (Exception ignored) {} + conn = openConnection(); + } + } + + private void initTable() { + try { + conn.createStatement().executeUpdate( + "CREATE TABLE IF NOT EXISTS quest_conditions (" + + "player TEXT NOT NULL, " + + "condition_key TEXT NOT NULL, " + + "value TEXT DEFAULT '1', " + + "timestamp INTEGER, " + + "PRIMARY KEY (player, condition_key))"); } catch (SQLException e) { e.printStackTrace(); } } + public void setCondition(String player, String condition) { + setConditionRaw(player, condition, "1"); + } + /** - * 查询条件值 + * 异步写入 — 不阻塞主线程 + * 使用 Bukkit 异步调度,在独立线程中执行 SQLite 写操作 */ - public Object getCondition( - String player, - String condition) { - try { - PreparedStatement ps = - conn.prepareStatement( - "SELECT value FROM " - + "quest_conditions " - + "WHERE player = ? " - + "AND " - + "condition_key = ?"); - ps.setString(1, player); - ps.setString(2, condition); - ResultSet rs = - ps.executeQuery(); - if (rs.next()) { - String val = - rs.getString("value"); - rs.close(); - ps.close(); - // 尝试解析为数字 + public void setConditionRaw(String player, + String condition, String value) { + final String sql = + "INSERT OR REPLACE INTO quest_conditions " + + "(player, condition_key, value, timestamp) " + + "VALUES (?, ?, ?, ?)"; + final long ts = System.currentTimeMillis(); + + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + writeSync(sql, player, condition, value, ts); + }); + } + + /** + * 在异步线程中执行写入,带重试 + */ + private void writeSync(String sql, String player, + String condKey, String value, long timestamp) { + synchronized (connLock) { + for (int attempt = 0; attempt < 5; attempt++) { try { - if (val.contains(".")) { - return Double.parseDouble( - val); + ensureConnection(); + if (conn == null) return; + PreparedStatement ps = conn.prepareStatement(sql); + ps.setString(1, player); + ps.setString(2, condKey); + ps.setString(3, value); + ps.setLong(4, timestamp); + ps.executeUpdate(); + ps.close(); + return; + } catch (SQLException e) { + String msg = e.getMessage(); + if (msg != null && (msg.contains("SQLITE_BUSY") + || msg.contains("database is locked"))) { + try { + Thread.sleep(200 + attempt * 200); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return; + } + } else { + e.printStackTrace(); + return; + } + } + } + } + } + + /** + * 同步读取(主线程) + * WAL 模式下读操作不阻塞写,可安全在主线程执行 + */ + public Object getCondition(String player, + String condition) { + synchronized (connLock) { + for (int attempt = 0; attempt < 3; attempt++) { + try { + ensureConnection(); + if (conn == null) return null; + PreparedStatement ps = conn.prepareStatement( + "SELECT value FROM quest_conditions " + + "WHERE player = ? " + + "AND condition_key = ?"); + ps.setString(1, player); + ps.setString(2, condition); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + String val = rs.getString("value"); + rs.close(); + ps.close(); + try { + if (val.contains(".")) + return Double.parseDouble(val); + return Long.parseLong(val); + } catch (NumberFormatException e) { + return val; + } + } + rs.close(); + ps.close(); + return null; + } catch (SQLException e) { + String msg = e.getMessage(); + if (msg != null && (msg.contains("SQLITE_BUSY") + || msg.contains("database is locked"))) { + try { + Thread.sleep(100 + attempt * 100); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return null; + } + } else { + e.printStackTrace(); + return null; } - return Long.parseLong(val); - } catch (NumberFormatException e) { - return val; } } - rs.close(); - ps.close(); - } catch (SQLException e) { - e.printStackTrace(); } return null; } public void close() { - try { - if (conn != null - && !conn.isClosed()) { - conn.close(); + synchronized (connLock) { + try { + if (conn != null && !conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); } - } catch (SQLException e) { - e.printStackTrace(); } } } diff --git a/src/main/java/Sdf1_login/QuestTracker.java b/src/main/java/Sdf1_login/QuestTracker.java index 228c1ac..ca63178 100644 --- a/src/main/java/Sdf1_login/QuestTracker.java +++ b/src/main/java/Sdf1_login/QuestTracker.java @@ -11,8 +11,13 @@ import java.util.Map; import java.util.TreeMap; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.generator.structure.Structure; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; // 如果 Bonds 在 Sdf1_login 包下: @@ -35,6 +40,8 @@ public class QuestTracker { checkInRecords = new ArrayList<>(); private final Map lastCheckInPos = new HashMap<>(); + private static boolean loggedStructureRegistry + = false; public void recordCheckIn( String player, @@ -80,11 +87,19 @@ public class QuestTracker { public QuestTracker(Plugin plugin) { this.plugin = plugin; this.storage = new QuestStorage( - plugin.getDataFolder()); + plugin.getDataFolder(), plugin); loadAllQuests(); } + // ========== 重载任务配置 ========== + public void reload() { + questCache.clear(); + loadAllQuests(); + plugin.getLogger().info( + "[QuestTracker] 任务配置已重载"); + } + public void debugPlayer(String player) { plugin.getLogger().info( "[QuestTracker] === " @@ -221,14 +236,27 @@ public class QuestTracker { "下界", "末地" }; - // ===== 结构映射 ===== + // ===== 结构映射(PaperMC 1.21.4 实际结构 key)===== private static final Map STRUCTURE_CATEGORY = new HashMap<>(); static { // 主世界结构 STRUCTURE_CATEGORY.put( - "minecraft:village", "村庄"); + "minecraft:village_plains", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_desert", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_savanna", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_snowy", + "村庄"); + STRUCTURE_CATEGORY.put( + "minecraft:village_taiga", + "村庄"); STRUCTURE_CATEGORY.put( "minecraft:desert_pyramid", "沙漠"); @@ -243,24 +271,39 @@ public class QuestTracker { "minecraft:mansion", "森林"); STRUCTURE_CATEGORY.put( "minecraft:pillager_outpost", - "平原"); + "掠夺者前哨站"); STRUCTURE_CATEGORY.put( "minecraft:mineshaft", "地下"); STRUCTURE_CATEGORY.put( - "minecraft:stronghold", "地下"); + "minecraft:mineshaft_mesa", + "地下"); + // 要塞是独立的探索结构(通往末地的传送门所在地) + STRUCTURE_CATEGORY.put( + "minecraft:stronghold", + "末地要塞"); + // 远古城市是结构,应分类为"远古城市"而非"地下" STRUCTURE_CATEGORY.put( "minecraft:ancient_city", - "地下"); + "远古城市"); STRUCTURE_CATEGORY.put( "minecraft:trail_ruins", - "平原"); + "远古遗迹"); STRUCTURE_CATEGORY.put( "minecraft:trial_chambers", - "地下"); + "试炼密室"); STRUCTURE_CATEGORY.put( - "minecraft:ocean_ruin", "海洋"); + "minecraft:monument", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_cold", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ocean_ruin_warm", + "海洋"); STRUCTURE_CATEGORY.put( "minecraft:shipwreck", "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:shipwreck_beached", + "海洋"); STRUCTURE_CATEGORY.put( "minecraft:buried_treasure", "海洋"); @@ -268,14 +311,23 @@ public class QuestTracker { "minecraft:ruined_portal", "平原"); STRUCTURE_CATEGORY.put( - "minecraft:fossil", "平原"); + "minecraft:ruined_portal_desert", + "沙漠"); STRUCTURE_CATEGORY.put( - "minecraft:ancient_city", - "地下"); + "minecraft:ruined_portal_jungle", + "森林"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_mountain", + "山地"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_ocean", + "海洋"); + STRUCTURE_CATEGORY.put( + "minecraft:ruined_portal_swamp", + "沼泽"); // 下界结构 STRUCTURE_CATEGORY.put( - "minecraft:nether_fortress", - "下界"); + "minecraft:fortress", "下界"); STRUCTURE_CATEGORY.put( "minecraft:bastion_remnant", "下界"); @@ -283,8 +335,11 @@ public class QuestTracker { "minecraft:nether_fossil", "下界"); STRUCTURE_CATEGORY.put( - "minecraft:basalt_pillars", + "minecraft:ruined_portal_nether", "下界"); + // 末地结构 + STRUCTURE_CATEGORY.put( + "minecraft:end_city", "末地"); } // 所有群系分类的条件关键词映射 @@ -312,6 +367,40 @@ public class QuestTracker { CATEGORY_KEYWORDS.put("沙滩", "石岸"); CATEGORY_KEYWORDS.put("海岸", "石岸"); + // ===== 结构名称关键词映射 ===== + // 主世界结构 + CATEGORY_KEYWORDS.put("村庄", "村庄"); + CATEGORY_KEYWORDS.put("沙漠神殿", "沙漠"); + CATEGORY_KEYWORDS.put("丛林神庙", "森林"); + CATEGORY_KEYWORDS.put("沼泽小屋", "沼泽"); + CATEGORY_KEYWORDS.put("雪屋", "雪原"); + CATEGORY_KEYWORDS.put("林地府邸", "森林"); + CATEGORY_KEYWORDS.put("掠夺者前哨站", "平原"); + CATEGORY_KEYWORDS.put("废弃矿井", "地下"); + CATEGORY_KEYWORDS.put("要塞", "末地要塞"); + CATEGORY_KEYWORDS.put("末地要塞", "末地要塞"); + CATEGORY_KEYWORDS.put("远古城市", "远古城市"); + CATEGORY_KEYWORDS.put("古迹废墟", "地下"); + CATEGORY_KEYWORDS.put("试炼密室", "地下"); + CATEGORY_KEYWORDS.put("海底废墟", "海洋"); + CATEGORY_KEYWORDS.put("沉船", "海洋"); + CATEGORY_KEYWORDS.put("埋藏的宝箱", "海洋"); + CATEGORY_KEYWORDS.put("废弃传送门", "主世界"); + CATEGORY_KEYWORDS.put("化石", "主世界"); + + // 下界结构 + CATEGORY_KEYWORDS.put("下界要塞", "下界"); + CATEGORY_KEYWORDS.put("猪灵堡垒", "下界"); + CATEGORY_KEYWORDS.put("玄武岩结构", "下界"); + + // 末地结构 + CATEGORY_KEYWORDS.put("末地城", "末地"); + CATEGORY_KEYWORDS.put("末地船", "末地"); + + // 下界相关别名 + CATEGORY_KEYWORDS.put("地狱要塞", "下界"); + CATEGORY_KEYWORDS.put("下界堡垒", "下界"); + } /** @@ -367,6 +456,223 @@ public class QuestTracker { return "主世界"; } + // ========== 结构检测 ========== + + /** + * 检测指定位置是否存在自然生成的结构 + * 返回结构分类(如"村庄"、"沙漠"等),未找到返回null + * 使用 PaperMC World.hasStructureAt() + Registry.STRUCTURE API + */ + private String detectStructureAt( + String worldName, double x, + double y, double z) { + try { + World w = Bukkit.getWorld(worldName); + if (w == null) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测: 世界 '" + + worldName + "' 不存在"); + return null; + } + + int bx = (int) Math.floor(x); + int by = (int) Math.floor(y); + int bz = (int) Math.floor(z); + + // 首次调用时打印所有注册表结构键 + if (!loggedStructureRegistry) { + plugin.getLogger().info( + "[QuestTracker] === 结构注册表诊断 ==="); + int count = 0; + for (Structure s : Registry.STRUCTURE) { + NamespacedKey key = + Registry.STRUCTURE.getKey(s); + plugin.getLogger().info( + "[QuestTracker] 注册表结构 #" + + count + ": " + key); + count++; + } + plugin.getLogger().info( + "[QuestTracker] 注册表共 " + + count + " 个结构"); + plugin.getLogger().info( + "[QuestTracker] === 诊断结束 ==="); + loggedStructureRegistry = true; + } + + // 第1步: 精确位置检查 + io.papermc.paper.math.Position exactPos = + io.papermc.paper.math.Position.block( + bx, by, bz); + for (Structure structure : Registry.STRUCTURE) { + boolean hasAt = + w.hasStructureAt(exactPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY.get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(精确): " + + keyStr + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + + // 第2步: Y轴偏移检查 (-5 到 +10) + for (int dy = -5; dy <= 10; dy++) { + if (dy == 0) continue; + int testY = by + dy; + io.papermc.paper.math.Position testPos = + io.papermc.paper.math.Position.block( + bx, testY, bz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + testPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey(structure); + String keyStr = (key != null) + ? key.toString() : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(Y偏移" + + dy + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat : "未映射")); + if (cat != null) return cat; + } + } + } + + // 第3步: 水平偏移检查 (±1格) + for (int dx = -1; dx <= 1; dx++) { + for (int dz = -1; dz <= 1; dz++) { + if (dx == 0 && dz == 0) continue; + io.papermc.paper.math.Position offsetPos = + io.papermc.paper.math.Position + .block(bx + dx, by, + bz + dz); + for (Structure structure + : Registry.STRUCTURE) { + boolean hasAt = w.hasStructureAt( + offsetPos, structure); + if (hasAt) { + NamespacedKey key = + Registry.STRUCTURE + .getKey( + structure); + String keyStr = (key != null) + ? key.toString() + : "unknown"; + String cat = + STRUCTURE_CATEGORY + .get(keyStr); + plugin.getLogger().info( + "[QuestTracker] 结构匹配(偏移" + + dx + "," + + dz + "): " + + keyStr + + " -> category=" + + (cat != null + ? cat + : "未映射")); + if (cat != null) return cat; + } + } + } + } + + plugin.getLogger().info( + "[QuestTracker] 所有位置检查完毕, " + + "未匹配任何结构 (pos=" + + bx + "," + by + "," + bz + + ")"); + } catch (Exception ex) { + plugin.getLogger().warning( + "[QuestTracker] 结构检测异常: " + + ex.getClass().getSimpleName() + + ": " + ex.getMessage()); + ex.printStackTrace(); + } + return null; + } + + /** + * 将结构中文名映射到对应的大类 + * 用于匹配任务条件中的结构名 + */ + private String mapStructureCategory( + String structureName) { + if (structureName == null) return ""; + + // 主世界结构 + if (structureName.contains("村庄")) + return "村庄"; + if (structureName.contains("沙漠神殿")) + return "沙漠"; + if (structureName.contains("丛林神庙") || structureName.contains("丛林神殿")) + return "森林"; + if (structureName.contains("沼泽小屋")) + return "沼泽"; + if (structureName.contains("雪屋") || structureName.contains("冰屋")) + return "雪原"; + if (structureName.contains("林地府邸")) + return "森林"; + if (structureName.contains("掠夺者前哨站")) + return "掠夺者前哨站"; + if (structureName.contains("废弃矿井") || structureName.contains("恶地废弃矿井")) + return "地下"; + // 要塞是独立的探索结构 + if (structureName.contains("要塞")) + return "末地要塞"; + // 远古城市是结构,应分类为"远古城市" + if (structureName.contains("远古城市")) + return "远古城市"; + if (structureName.contains("远古遗迹") || structureName.contains("古迹废墟")) + return "远古遗迹"; + if (structureName.contains("试炼密室")) + return "试炼密室"; + if (structureName.contains("海底神殿") || structureName.contains("海洋神殿")) + return "海洋"; + if (structureName.contains("海底废墟") || structureName.contains("冻洋废墟") || structureName.contains("暖洋废墟")) + return "海洋"; + if (structureName.contains("沉船")) + return "海洋"; + if (structureName.contains("埋藏的宝箱") || structureName.contains("埋藏的宝藏")) + return "海洋"; + if (structureName.contains("废弃传送门")) + return "平原"; + if (structureName.contains("化石")) + return "主世界"; + + // 下界结构 + if (structureName.contains("下界要塞")) + return "下界"; + if (structureName.contains("猪灵堡垒") || structureName.contains("堡垒遗迹")) + return "下界"; + if (structureName.contains("玄武岩结构") || structureName.contains("下界化石")) + return "下界"; + + // 末地结构 + if (structureName.contains("末地城")) + return "末地"; + if (structureName.contains("末地船")) + return "末地"; + + return ""; + } // ========== 解析为区块 ========== @@ -1668,6 +1974,24 @@ public class QuestTracker { + total + "/1"; } + // 打卡结构条件:打卡X个结构名 → 显示进度 + if (key.contains("打卡") + && !key.contains("群系") + && !key.contains("风景")) { + String target = extractBiome(key); + if (!target.isEmpty()) { + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; + int cur = getCounter(player, + "biomecat_" + target); + boolean done = cur >= needed; + return (done ? "§a" : "§7") + + "打卡 " + target + ": §e" + + Math.min(cur, needed) + + "/" + needed; + } + } + if (key.contains("完成") && key.contains("阶段")) { boolean done = checkStageDependency( @@ -1739,11 +2063,19 @@ public class QuestTracker { Object val = getDb().getField( player, - "checkin_streak"); + "login_streak"); + if (val == null || (val instanceof Number && ((Number) val).intValue() == 0)) { + // 兼容旧数据:如果没有login_streak则读取checkin_streak + val = getDb().getField( + player, + "checkin_streak"); + } int streak = val instanceof Number ? ((Number) val).intValue() : 0; - return "§7连续登录: §e" + boolean done = streak >= needed; + return (done ? "§a" : "§7") + + "连续登录: §e" + streak + "/" + needed + "天"; } @@ -1887,7 +2219,12 @@ public class QuestTracker { if (needed <= 0) needed = 1; int streak = getIntField( player, - "checkin_streak"); + "login_streak"); + if (streak == 0) { + streak = getIntField( + player, + "checkin_streak"); + } if (streak == 0) { streak = getIntField( player, "streak"); @@ -1897,7 +2234,9 @@ public class QuestTracker { player, "total_checkin_days"); } - return "§7近7天登录: §e" + boolean done = streak >= needed; + return (done ? "§a" : "§7") + + "近7天登录: §e" + streak + "/" + needed + "天"; } @@ -2179,23 +2518,58 @@ public class QuestTracker { String category = mapBiomeCategory( rawBiomeKey, worldName); - // 结构检测(覆盖纯生物群系分类) - // 地下检测:Y低于表面则归类为地下 -// 覆盖废弃矿井/试炼密室/要塞/远古城市等 + + // ===== 结构检测优先于Y轴"地下"判断 ===== + // 要塞等结构可生成在任何主世界群系下,不应固定为"地下" + // 使用 PaperMC API 检测玩家所在位置的结构 + String structureCat = null; try { - org.bukkit.World uw = - Bukkit.getWorld(worldName); - if (uw != null) { - int surfaceY = uw - .getHighestBlockYAt( - (int) Math.floor(x), - (int) Math.floor(z)); - if (y < surfaceY - 8) { - category = "地下"; - } - } + structureCat = detectStructureAt( + worldName, x, y, z); } catch (Exception ignored) {} + if (structureCat != null + && !structureCat.isEmpty() + && !"地下".equals(structureCat)) { + // 结构检测成功且非"地下"类,结构分类优先 + category = structureCat; + plugin.getLogger().info( + "[QuestTracker] 结构覆盖: " + + category + + " (结构: " + + structureCat + ")"); + } else { + // 未检测到有效结构,使用Y轴检测判定地下 + // 地下检测:Y低于表面则归类为地下 + try { + org.bukkit.World uw = + Bukkit.getWorld(worldName); + if (uw != null) { + int surfaceY = uw + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } + } + } catch (Exception ignored) {} + if (structureCat != null + && !structureCat.isEmpty()) { + plugin.getLogger().info( + "[QuestTracker] 结构" + + structureCat + + " 使用Y轴判定"); + } else { + plugin.getLogger().info( + "[QuestTracker] 未检测到结构: world=" + + worldName + ", pos=" + + String.format( + "%.0f,%.0f,%.0f", + x, y, z)); + } + } + // 记录总打卡 int allCur = getCounter( @@ -2203,10 +2577,11 @@ public class QuestTracker { setCounter(player, "biome_ALL", allCur + 1); - // 记录分类打卡 + // 记录分类打卡(群系,排除结构避免重复计数) if (!category.isEmpty() - && !"主世界" - .equals(category)) { + && !"主世界".equals(category) + && (structureCat == null + || !category.equals(structureCat))) { String catKey = "biomecat_" + category; int catCur = getCounter( @@ -2215,6 +2590,17 @@ public class QuestTracker { catCur + 1); } + // 记录结构打卡(结构单独计数) + if (structureCat != null + && !structureCat.isEmpty()) { + String structKey = + "biomecat_" + structureCat; + int structCur = getCounter( + player, structKey); + setCounter(player, structKey, + structCur + 1); + } + // 记录具体群系 String bioKey = "biome_" + rawBiomeKey; @@ -2244,12 +2630,61 @@ public class QuestTracker { String condKey = stripOptional(cond); if (!condKey - .contains("打卡") - || !condKey - .contains("群系")) + .contains("打卡")) + continue; + // 支持群系和结构两种打卡条件 + boolean isBiomeCond = + condKey.contains("群系") + || condKey + .contains("风景点"); + boolean isStructureCond = + condKey.contains("结构") + || condKey + .contains("村庄") + || condKey + .contains("神殿") + || condKey + .contains("神庙") + || condKey + .contains("小屋") + || condKey + .contains("雪屋") + || condKey + .contains("府邸") + || condKey + .contains("前哨站") + || condKey + .contains("矿井") + || condKey + .contains("要塞") + || condKey + .contains("城市") + || condKey + .contains("废墟") + || condKey + .contains("密室") + || condKey + .contains("沉船") + || condKey + .contains("宝箱") + || condKey + .contains("传送门") + || condKey + .contains("化石") + || condKey + .contains("堡垒"); + if (!isBiomeCond + && !isStructureCond) continue; String target = extractBiome(condKey); + plugin.getLogger().info( + "[QuestTracker] 条件匹配: condKey=\"" + + condKey + "\", target=\"" + + target + "\", category=\"" + + category + "\", isBiomeCond=" + + isBiomeCond + ", isStructureCond=" + + isStructureCond); boolean hit = false; if ("ALL".equals(target) || "风景点" @@ -2296,6 +2731,7 @@ public class QuestTracker { // ===== 获取群系和分类 ===== String biome = "minecraft:plains"; String category = "平原"; + String structureCat = null; try { org.bukkit.World w = Bukkit.getWorld(worldName); @@ -2312,12 +2748,29 @@ public class QuestTracker { category = mapBiomeCategory( biome, worldName); - int surfaceY = w - .getHighestBlockYAt( - (int) Math.floor(x), - (int) Math.floor(z)); - if (y < surfaceY - 8) { - category = "地下"; + // ===== 结构检测优先于Y轴"地下"判断 ===== + structureCat = + detectStructureAt( + worldName, x, y, z); + plugin.getLogger().info( + "[QuestTracker] checkInAtPosition 结构检测: " + + structureCat + " at " + + String.format("%.0f,%.0f,%.0f", x, y, z)); + + if (structureCat != null + && !structureCat.isEmpty() + && !"地下".equals(structureCat)) { + // 结构检测成功且非"地下"类,结构分类优先 + category = structureCat; + } else { + // 未检测到有效结构,使用Y轴检测判定地下 + int surfaceY = w + .getHighestBlockYAt( + (int) Math.floor(x), + (int) Math.floor(z)); + if (y < surfaceY - 8) { + category = "地下"; + } } if ("下界".equals(category) @@ -2362,6 +2815,9 @@ public class QuestTracker { StringBuilder sb = new StringBuilder(); sb.append("§a打卡成功!群系: §e") .append(category); + if (structureCat != null && !structureCat.isEmpty()) { + sb.append("\n§a结构: §e").append(structureCat); + } if (!matched.isEmpty()) { sb.append("\n§a完成任务条件:"); for (String m : matched) { @@ -2616,9 +3072,19 @@ public class QuestTracker { * "打卡任意海洋群系" → "海洋" * "打卡任意群系" → "ALL" * "打卡任意风景点" → "" + * "打卡村庄" → "村庄" + * "打卡沙漠神殿" → "沙漠" */ private String extractBiome(String text) { if (text == null) return ""; + + // 优先匹配结构名称(更具体) + String structureResult = + mapStructureCategory(text); + if (!structureResult.isEmpty()) { + return structureResult; + } + // 逐个匹配分类关键词 for (Map.Entry en : CATEGORY_KEYWORDS.entrySet()) { @@ -2766,22 +3232,43 @@ public class QuestTracker { if (key.contains("打卡") && (key.contains("群系") - || key.contains("风景点"))) { + || key.contains("风景点") + || key.contains("结构") + || key.contains("村庄") + || key.contains("神殿") + || key.contains("神庙") + || key.contains("小屋") + || key.contains("雪屋") + || key.contains("府邸") + || key.contains("前哨站") + || key.contains("矿井") + || key.contains("要塞") + || key.contains("城市") + || key.contains("废墟") + || key.contains("密室") + || key.contains("废墟") + || key.contains("沉船") + || key.contains("宝箱") + || key.contains("传送门") + || key.contains("化石") + || key.contains("堡垒"))) { String biome = extractBiome(key); + int needed = parseNumberFromText(key); + if (needed <= 0) needed = 1; if ("ALL".equals(biome)) { int total = getCounter( player, "biome_ALL"); - return total >= 1; + return total >= needed; } if (!biome.isEmpty()) { int catCount = getCounter( player, "biomecat_" + biome); - return catCount >= 1; + return catCount >= needed; } return getCounter( - player, "biome_ALL") >= 1; + player, "biome_ALL") >= needed; } if (key.contains("完成") @@ -2841,7 +3328,12 @@ public class QuestTracker { Object val = getDb().getField( player, - "checkin_streak"); + "login_streak"); + if (val == null || (val instanceof Number && ((Number) val).intValue() == 0)) { + val = getDb().getField( + player, + "checkin_streak"); + } int streak = val instanceof Number ? ((Number) val).intValue() : 0; @@ -2930,7 +3422,12 @@ public class QuestTracker { if (needed <= 0) needed = 1; int streak = getIntField( player, - "checkin_streak"); + "login_streak"); + if (streak == 0) { + streak = getIntField( + player, + "checkin_streak"); + } if (streak == 0) { streak = getIntField( player, "streak"); diff --git a/src/main/java/Sdf1_login/RadioDownloadListener.java b/src/main/java/Sdf1_login/RadioDownloadListener.java index f2e10c2..0d247e6 100644 --- a/src/main/java/Sdf1_login/RadioDownloadListener.java +++ b/src/main/java/Sdf1_login/RadioDownloadListener.java @@ -80,11 +80,11 @@ public class RadioDownloadListener log.warning("[Radio] " + p.getName() + " ✗ 拒绝了资源包"); - log.warning("[Radio] " + /* log.warning("[Radio] " + "可能原因: " + "1.客户端设置禁用资源包" + " 2.已缓存拒绝记录" - + " 3.SSL证书不匹配"); + + " 3.SSL证书不匹配");*/ handleFailure(p, uuid); break; @@ -92,12 +92,12 @@ public class RadioDownloadListener log.warning("[Radio] " + p.getName() + " ✗ 下载失败"); - log.warning("[Radio] " + /* log.warning("[Radio] " + "可能原因: " + "1.URL无法访问" + " 2.SSL证书错误" + " 3.网络超时" - + " 4.文件不存在(404)"); + + " 4.文件不存在(404)");*/ handleFailure(p, uuid); break; @@ -124,9 +124,8 @@ public class RadioDownloadListener + "玩家继续留在服务器"); // 不踢出,只记录熔断,玩家正常游玩 p.sendMessage("§e§l[Radio] 资源包加载失败" - + ",跳过资源包,部分画面、音频可能出现问题" - + "\n§d§l若想继续下载,请退出重进" + - "\n§b§l若持续失败,请重启客户端"); + + ",已取消下载资源包,部分画面音频可能出现问题" + + "\n§d§l若想继续下载,请退出重进。若持续失败,请上报管理员"); return; } diff --git a/src/main/java/Sdf1_login/ShopItem.java b/src/main/java/Sdf1_login/ShopItem.java index 422c083..d336c7c 100644 --- a/src/main/java/Sdf1_login/ShopItem.java +++ b/src/main/java/Sdf1_login/ShopItem.java @@ -11,7 +11,6 @@ public class ShopItem { private int stock; private int hourlySales; private int totalSales; -//打包了 直接测试替换就好了。 public ShopItem(String id, String displayName, Material material, int buyPrice, int sellPrice, int stock, diff --git a/src/main/java/Sdf1_login/ShopManager.java b/src/main/java/Sdf1_login/ShopManager.java index e18b388..cdcc045 100644 --- a/src/main/java/Sdf1_login/ShopManager.java +++ b/src/main/java/Sdf1_login/ShopManager.java @@ -223,8 +223,8 @@ public class ShopManager implements Listener { } } br.close(); - plugin.getLogger().info("[Shop] NBT数据: " - + itemStackCache.size() + "条"); + // plugin.getLogger().info("[Shop] NBT数据: " + // + itemStackCache.size() + "条"); } catch (Exception ignored) {} } @@ -552,9 +552,9 @@ public class ShopManager implements Listener { .sendReceiptChat(p, latestOrder); } } catch (Exception ex) { - plugin.getLogger().warning( - "[Shop] 记录订单失败: " - + ex.getMessage()); + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + // + ex.getMessage()); } // ===== 发货 ===== @@ -899,7 +899,7 @@ public class ShopManager implements Listener { // ===== 加载分类 ===== public void loadCategories() { - plugin.getLogger().info("[Shop] === loadCategories 被调用 ==="); + // plugin.getLogger().info("[Shop] === loadCategories 被调用 ==="); // 过滤调试日志 categories.clear(); viewCat.clear(); // ★ 清缓存 viewPage.clear(); @@ -920,24 +920,24 @@ public class ShopManager implements Listener { && !n.equals("coupons.md")); } if (allMd == null) { - plugin.getLogger().warning( - "[Shop] shop目录为空"); + // plugin.getLogger().warning( + // "[Shop] shop目录为空"); return; } - plugin.getLogger().info("[Shop] === 开始加载 ==="); + /* plugin.getLogger().info("[Shop] === 开始加载 ==="); plugin.getLogger().info("[Shop] 扫描到 " + allMd.length + " 个md文件"); - +*/ for (File f : allMd) { - plugin.getLogger().info("[Shop] 读取: " - + f.getName()); + /* plugin.getLogger().info("[Shop] 读取: " + + f.getName());*/ ShopCategory cat = parseFile(f); if (cat != null) { - plugin.getLogger().info("[Shop] 分类: " + /* plugin.getLogger().info("[Shop] 分类: " + cat.getName() + " 商品数: " - + cat.getItems().size()); + + cat.getItems().size());*/ if (!cat.getItems().isEmpty()) { categories.add(cat); } @@ -1011,14 +1011,14 @@ public class ShopManager implements Listener { pw.close(); txt.delete(); - plugin.getLogger().info( - "[Shop] 已迁移: " - + txt.getName() - + " → " + mdName); + // plugin.getLogger().info( + // "[Shop] 已迁移: " + // + txt.getName() + // + " → " + mdName); } catch (Exception e) { - plugin.getLogger().warning( - "[Shop] 迁移失败: " - + txt.getName()); + // plugin.getLogger().warning( + // "[Shop] 迁移失败: " + // + txt.getName()); } } } @@ -1077,9 +1077,9 @@ public class ShopManager implements Listener { } br.close(); } catch (Exception e) { - plugin.getLogger().warning( - "[Shop] 解析失败: " + f.getName() - + " 原因: " + e.getMessage()); + // plugin.getLogger().warning( + // "[Shop] 解析失败: " + f.getName() + // + " 原因: " + e.getMessage()); return null; } return cat; @@ -1088,7 +1088,7 @@ public class ShopManager implements Listener { // ===== 解析表格行 ===== private ShopItem parseTableRow(String row) { - plugin.getLogger().info("[Shop] 行: " + row); + // plugin.getLogger().info("[Shop] 行: " + row); // 过滤调试日志 String trimmed = row.trim(); if (trimmed.startsWith("|")) @@ -1098,12 +1098,11 @@ public class ShopManager implements Listener { trimmed.length() - 1); String[] p = trimmed.split("\\|"); - plugin.getLogger().info("[Shop] 列数=" - + p.length); + // plugin.getLogger().info("[Shop] 列数=" + p.length); // 过滤调试日志 if (p.length < 6) { - plugin.getLogger().warning( - "[Shop] ✗ 列数不足, 跳过"); + // plugin.getLogger().warning( + // "[Shop] ✗ 列数不足, 跳过"); return null; } @@ -1114,18 +1113,18 @@ public class ShopManager implements Listener { String rawSell = p[4].trim(); String rawStock = p[5].trim(); - plugin.getLogger().info("[Shop] ID=" - + rawId + " 名=" + rawName - + " 材质=" + rawMat - + " 购=" + rawBuy + " 售=" + rawSell - + " 库=" + rawStock); + // plugin.getLogger().info("[Shop] ID=" + // + rawId + " 名=" + rawName + // + " 材质=" + rawMat + // + " 购=" + rawBuy + " 售=" + rawSell + // + " 库=" + rawStock); // 过滤调试日志 // 检查材质是否存在 Material mat = Material.matchMaterial(rawMat); if (mat == null) { - plugin.getLogger().warning( - "[Shop] ✗ 无效材质: " + rawMat - + " (行: " + row + ")"); + // plugin.getLogger().warning( + // "[Shop] ✗ 无效材质: " + rawMat + // + " (行: " + row + ")"); return null; } @@ -1141,13 +1140,12 @@ public class ShopManager implements Listener { p.length > 7 ? Integer.parseInt( p[7].trim()) : 0); - plugin.getLogger().info( - "[Shop] ✓ 解析成功: " + rawId); + // plugin.getLogger().info("[Shop] ✓ 解析成功: " + rawId); // 过滤调试日志 return item; } catch (Exception e) { - plugin.getLogger().warning( - "[Shop] ✗ 数字解析失败: " - + e.getMessage()); + // plugin.getLogger().warning( + // "[Shop] ✗ 数字解析失败: " + // + e.getMessage()); return null; } } @@ -1189,9 +1187,9 @@ public class ShopManager implements Listener { pw.flush(); pw.close(); } catch (Exception e) { - plugin.getLogger().warning( - "[Shop] 保存失败: " - + cat.getFileName()); + // plugin.getLogger().warning( + // "[Shop] 保存失败: " + // + cat.getFileName()); } } @@ -1259,8 +1257,8 @@ public class ShopManager implements Listener { + "| BREAD | 面包 | BREAD" + " | 8 | 3 | -1 | 0 | 0 |\n"); - plugin.getLogger().info( - "[Shop] 已创建3个示例分类(md)"); + // plugin.getLogger().info( + // "[Shop] 已创建3个示例分类(md)"); } private void writeMd(File dir, String name, @@ -1276,8 +1274,8 @@ public class ShopManager implements Listener { pw.flush(); pw.close(); } catch (Exception e) { - plugin.getLogger().warning( - "[Shop] 创建失败: " + name); + // plugin.getLogger().warning( + // "[Shop] 创建失败: " + name); } } @@ -1402,8 +1400,8 @@ public class ShopManager implements Listener { public boolean buyItem(Player p, ShopItem item, int amount) { - plugin.getLogger().info("[Shop] ★ buyItem item=" + (item != null ? item.getId() : "null") + " amount=" + amount); - plugin.getLogger().info("buyItem被调用"); + // plugin.getLogger().info("[Shop] ★ buyItem item=" + (item != null ? item.getId() : "null") + " amount=" + amount); + // plugin.getLogger().info("buyItem被调用"); if (item == null) return false; if (item.getStock() >= 0 && item.getStock() < amount) { @@ -1525,8 +1523,8 @@ public class ShopManager implements Listener { p, all.get(all.size() - 1)); } } catch (Exception ex) { - plugin.getLogger().warning( - "[Shop] 记录订单失败: " + ex.getMessage()); + // plugin.getLogger().warning( + // "[Shop] 记录订单失败: " + ex.getMessage()); } return true; @@ -2484,7 +2482,7 @@ public class ShopManager implements Listener { int itemIdx = slotToIndex(raw); if (itemIdx < 0) { - plugin.getLogger().info("[Shop] ★ slot=" + raw + " 不是商品格"); + // plugin.getLogger().info("[Shop] ★ slot=" + raw + " 不是商品格"); return true; } int actual = itemIdx + pg * PAGE_SIZE; diff --git a/src/main/java/Sdf1_login/TicketManager.java b/src/main/java/Sdf1_login/TicketManager.java index c21edc2..039e8eb 100644 --- a/src/main/java/Sdf1_login/TicketManager.java +++ b/src/main/java/Sdf1_login/TicketManager.java @@ -163,7 +163,7 @@ public class TicketManager { "§7类型: §f" + type, "§7状态: " + stxt(status), "§7提交者: §f" + requester, - "§7奖励: §e" + reward + " 积分")); + "§7奖励: §e" + reward + " 债券")); g.setItem(12, mk(Material.BOOK, "§7详细描述", @@ -242,6 +242,13 @@ public class TicketManager { "§a标记完结", "§7确认工单处理完成")); } + // ★ 服务商上报完结后:管理员确认完结 + if ("completed".equals(status)) { + g.setItem(19, mk(Material.LIME_DYE, + "§a确认完结", + "§7确认服务商处理完成", + "§7输入评分1-5")); + } // 驳回(非已关闭/已完结状态) if (!"closed".equals(status) && !"resolved".equals(status) @@ -410,7 +417,7 @@ public class TicketManager { "§a§l[工单] §f创建成功!" + " #§e" + id + " 奖励: §e" - + price + "积分"); + + price + "债券"); } notifyAdmins(p.getName(), id); } else { @@ -1017,23 +1024,18 @@ public class TicketManager { // 管理员:标记完结(已回复状态) if (adm(p) && "replied".equals(st) && asg.equals("admin")) { - - // 管理员:标记完结(已回复状态) - if (adm(p) && "replied".equals(st) - && asg.equals("admin")) { - Map tk = - plugin.getDb().getTicket(id); - String tkType = sv(tk, "type"); - if (TYPE_DELETE.equals(tkType)) { - approveDeleteTicket(p, id); - return true; - } - plugin.getDb().completeTicket(id); - p.sendMessage("§a工单 #" + id - + " 已标记完结"); - openDetail(p, id); + Map tk = + plugin.getDb().getTicket(id); + String tkType = sv(tk, "type"); + if (TYPE_DELETE.equals(tkType)) { + approveDeleteTicket(p, id); return true; } + plugin.getDb().completeTicket(id); + p.sendMessage("§a工单 #" + id + + " 已标记完结"); + openDetail(p, id); + return true; } // 服务商:抢单 if (plugin.getDb() @@ -1044,6 +1046,19 @@ public class TicketManager { openDetail(p, id); return true; } + // ★ 管理员:确认服务商完结(输入评分) + if (adm(p) && "completed".equals(st)) { + p.closeInventory(); + plugin.getChatInput() + .getState(p).type = + ChatInputManager.InputType + .TICKET_SCORE; + plugin.getChatInput() + .getState(p).ticketId = id; + p.sendMessage( + "§e输入评分1-5:"); + return true; + } // 服务商:标记完结 if (plugin.getDb() .isServiceProvider(p.getName()) @@ -1263,44 +1278,34 @@ public class TicketManager { int score = nv(t, "score"); ConfigManager cfg = plugin.getConfig2(); + + // ★ 提交者获得工单奖励(债券) if (reward > 0) { - plugin.getDb().addPoints( - requester, reward); + plugin.getBonds().addBonds( + requester, reward, + BondManager.TX_TICKET_REWARD, "", + "System", "工单 #" + tid + " 奖励"); Player rp = Bukkit.getPlayer( requester); if (rp != null && rp.isOnline()) { rp.sendMessage( "§e§l[工单] §f获得 " + reward - + " 积分奖励"); + + " 债券奖励"); } } + + // ★ 服务商获得债券奖励 if (!assigned.isEmpty()) { - int pts = (int) (score + int bondReward = (int) (score * cfg.providerPointsPerScore); - if (pts > 0) { - plugin.getDb().addPoints( - assigned, pts); - } - double money = - cfg.baseEconomyReward - + score - * cfg.providerEconomyPerScore; - boolean ok = false; - try { - if (plugin.getEconomy() != null) { - OfflinePlayer op = - Bukkit.getOfflinePlayer( - assigned); - plugin.getEconomy() - .depositPlayer( - op, money); - ok = true; - } - } catch (Exception e) { - plugin.getLogger().warning( - "[Sdf1_login] 经济失败: " - + e.getMessage()); + if (bondReward > 0) { + plugin.getBonds().addBonds( + assigned, bondReward, + BondManager.TX_TICKET_SERVICE, "", + "System", "工单 #" + tid + + " 服务奖励(评分" + + score + ")"); } Player pp = Bukkit.getPlayer(assigned); @@ -1309,13 +1314,9 @@ public class TicketManager { "§a§l[工单] §f工单 #" + tid + " 确认完结"); - if (pts > 0) - pp.sendMessage("§7积分: §e" - + pts); - if (ok) - pp.sendMessage("§7经济: §a$" - + String.format( - "%.0f", money)); + if (bondReward > 0) + pp.sendMessage("§7债券: §e" + + bondReward + " 枚"); } } } @@ -1559,7 +1560,7 @@ public class TicketManager { lore.add("§7提交者: §f" + requester); lore.add("§7奖励: §e" + reward - + " 积分"); + + " 债券"); lore.add(""); lore.add("§7点击查看详情"); im.setLore(lore); diff --git a/src/main/java/Sdf1_login/TreasureBridge.java b/src/main/java/Sdf1_login/TreasureBridge.java new file mode 100644 index 0000000..72fa3e4 --- /dev/null +++ b/src/main/java/Sdf1_login/TreasureBridge.java @@ -0,0 +1,116 @@ +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Sdf1_game寻宝系统桥接类 + * 用于Sdf1_login调用Sdf1_game的clearPlayerAllClaims方法 + */ +public class TreasureBridge { + + private Object treasureManager; + private Method clearPlayerAllClaimsMethod; + private boolean hooked = false; + + /** + * 桥接Sdf1_game插件 + * @return 是否桥接成功 + */ + public boolean hook() { + Plugin sdf1_game = Bukkit.getPluginManager() + .getPlugin("Sdf1_game"); + + if (sdf1_game == null || !sdf1_game.isEnabled()) { + return false; + } + + try { + // 获取Sdf1_game的Main实例 + Object mainInstance = sdf1_game; + + // 获取TreasureManager实例 + treasureManager = findTreasureManager(mainInstance); + if (treasureManager == null) { + return false; + } + + // 获取clearPlayerAllClaims方法 + clearPlayerAllClaimsMethod = treasureManager.getClass() + .getMethod("clearPlayerAllClaims", String.class); + + hooked = true; + return true; + } catch (Exception e) { + return false; + } + } + + /** + * 清空指定玩家的所有领取记录 + * @param playerName 玩家名 + * @return 清除的记录数量,失败返回-1 + */ + public int clearPlayerAllClaims(String playerName) { + if (!isHooked()) return -1; + try { + int result = (int) clearPlayerAllClaimsMethod.invoke( + treasureManager, playerName); + return result; + } catch (Exception e) { + return -1; + } + } + + /** + * 查找TreasureManager实例 + * @param mainInstance Sdf1_game的Main实例 + * @return TreasureManager实例 + */ + private Object findTreasureManager(Object mainInstance) { + Class c = mainInstance.getClass(); + + // 策略1: getTreasureManager() 方法 + try { + Method m = c.getMethod("getTreasureManager"); + Object r = m.invoke(mainInstance); + if (r != null) return r; + } catch (Exception ignored) { + } + + // 策略2: treasureManager 字段 + try { + Field f = c.getDeclaredField("treasureManager"); + f.setAccessible(true); + Object r = f.get(mainInstance); + if (r != null) return r; + } catch (Exception ignored) { + } + + // 策略3: 遍历字段按类型名查找 + try { + for (Field f : c.getDeclaredFields()) { + if (f.getType().getSimpleName() + .equals("TreasureManager")) { + f.setAccessible(true); + Object r = f.get(mainInstance); + if (r != null) return r; + } + } + } catch (Exception ignored) { + } + + return null; + } + + /** + * 是否已桥接 + * @return 桥接状态 + */ + public boolean isHooked() { + return hooked; + } +} \ No newline at end of file diff --git a/src/main/java/Sdf1_login/UpdateChecker.java b/src/main/java/Sdf1_login/UpdateChecker.java index c5c01bb..a4e6e55 100644 --- a/src/main/java/Sdf1_login/UpdateChecker.java +++ b/src/main/java/Sdf1_login/UpdateChecker.java @@ -1,523 +1,442 @@ -package Sdf1_login; - -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; - -public class UpdateChecker { - - private final Main plugin; - private final String currentVersion; - - // ===== 仓库信息 ===== - private static final String GITHUB_OWNER = - "ypsdf1"; - private static final String GITHUB_REPO = - "sdf1_login"; - private static final String GITEE_OWNER = - "nihaoshidifu"; - private static final String GITEE_REPO = - "sdf1_login"; - - - // API 地址 - private static final String GITHUB_API = - "https://api.github.com/repos/" - + GITHUB_OWNER + "/" - + GITHUB_REPO - + "/tags"; - private static final String GITEE_API = - "https://gitee.com/api/v5/repos/" - + GITEE_OWNER + "/" - + GITEE_REPO - + "/tags"; - - // 仓库页面链接(给用户看的) - private static final String GITHUB_LINK = - "https://github.com/" - + GITHUB_OWNER + "/" - + GITHUB_REPO + "/releases"; - private static final String GITEE_LINK = - "https://gitee.com/" - + GITEE_OWNER + "/" - + GITEE_REPO + "/releases"; - - - // 单次请求超时(毫秒) - private static final int TIMEOUT_MS = 3000; - - // 熔断阈值 - private static final int TRIP_THRESHOLD = 5; - - // ===== 熔断状态(内存态,重启自动清除) ===== - private volatile boolean circuitTripped = - false; - private volatile int failCount = 0; - - // ===== 自动检测禁用(持久化到文件) ===== - private volatile boolean autoCheckDisabled = - false; - private final File disabledFile; - - public UpdateChecker(Main plugin) { - this.plugin = plugin; - this.currentVersion = - plugin.getDescription() - .getVersion(); - this.disabledFile = new File( - plugin.getDataFolder(), - "update_disabled.dat"); - loadDisabledState(); - } - - // ========== 持久化禁用状态 ========== - - private void loadDisabledState() { - if (disabledFile.exists()) { - try (BufferedReader br = - new BufferedReader( - new FileReader( - disabledFile))) { - String line = br.readLine(); - if (line != null) { - autoCheckDisabled = - "true".equals( - line.trim()); - } - } catch (Exception ignored) { - } - } - } - - private void saveDisabledState() { - try (PrintWriter pw = - new PrintWriter( - new FileWriter( - disabledFile))) { - pw.println(autoCheckDisabled - ? "true" : "false"); - } catch (Exception ignored) { - } - } - - // ========== 公开方法 ========== - - /** - * onEnable 调用(异步静默) - * 熔断或禁用则直接跳过 - */ - public void checkOnEnable() { - if (autoCheckDisabled) return; - if (circuitTripped) return; - Bukkit.getScheduler() - .runTaskAsynchronously(plugin, - () -> doCheck(null, true)); - } - - /** - * /sdf1_login update 调用(异步) - * 忽略熔断和禁用状态,强制检测 - * 只有拿到版号才解除熔断 - */ - public void checkUpdate( - CommandSender sender) { - Bukkit.getScheduler() - .runTaskAsynchronously(plugin, - () -> - doCheck(sender, - false)); - } - - // ========== 核心检测逻辑 ========== - private void doCheck(CommandSender sender, - boolean isAuto) { - - if (isAuto) { - if (autoCheckDisabled - || circuitTripped) { - return; - } - } - - // 通道1:GitHub - CheckResult gh = - tryChannel(GITHUB_API, - "GitHub", GITHUB_LINK); - - // 通道2:Gitee - CheckResult ge = - tryChannel(GITEE_API, - "Gitee", GITEE_LINK); - - // 选择成功的结果 - CheckResult chosen = null; - String failMsg = null; - - if (gh.isSuccess()) { - chosen = gh; - } else if (ge.isSuccess()) { - chosen = ge; - } else { - // 双通道均失败 - failCount++; - // 拼接两个通道的失败原因 - StringBuilder sb = - new StringBuilder(); - if (gh.error != null) { - sb.append("§7GitHub: §c") - .append(gh.error); - } - if (ge.error != null) { - if (sb.length() > 0) - sb.append("\n"); - sb.append("§7Gitee: §c") - .append(ge.error); - } - if (failCount >= TRIP_THRESHOLD) { - circuitTripped = true; - plugin.getLogger().warning( - "[Sdf1_login] 更新检测" - + "已熔断,连续失败" - + failCount + "次"); - } - if (sender != null) { - final String msg = - sb.toString(); - final boolean tripped = - circuitTripped; - final int fc = failCount; - Bukkit.getScheduler() - .runTask(plugin, () -> { - sender.sendMessage( - "§c§l[更新] " - + "检测失败"); - sender.sendMessage(msg); - sender.sendMessage( - "§e失败次数: " - + fc + "/" - + TRIP_THRESHOLD); - if (tripped) { - sender.sendMessage( - "§c已熔断," - + "请重启服务器" - + "重置"); - } - }); - } - return; - } - - // 至少一个通道成功 - failCount = 0; - - // 检查禁用信号 - if (isDisableSignal(chosen.version)) { - autoCheckDisabled = true; - saveDisabledState(); - plugin.getLogger().info( - "[Sdf1_login] 远程版本信号" - + " \"" + chosen.version - + "\",自动更新检测已禁用"); - if (sender != null) { - Bukkit.getScheduler() - .runTask(plugin, () -> - sender.sendMessage( - "§e[更新] 远程已" - + "禁用自动" - + "更新检测")); - } - return; - } - - // 手动调用:成功才解除熔断和禁用 - if (!isAuto) { - if (circuitTripped) { - circuitTripped = false; - } - if (autoCheckDisabled) { - autoCheckDisabled = false; - saveDisabledState(); - } - } - - final String src = chosen.source; - final String lnk = chosen.link; - final String rv = chosen.version; - - Bukkit.getScheduler() - .runTask(plugin, () -> { - if (sender == null) return; - - sender.sendMessage( - "§e[更新] 检测成功" - + " via: §a" + src); - sender.sendMessage( - "§e[更新] 本地版本:" - + " §f" - + currentVersion); - sender.sendMessage( - "§e[更新] 远程版本:" - + " §f" + rv); - - if (isNewer(rv, - currentVersion)) { - sender.sendMessage( - "§c[更新] 发现新" - + "版本: " + rv); - sender.sendMessage( - "§a请前往仓库下载" - + "更新:"); - sender.sendMessage( - "§a" + lnk); - } else { - sender.sendMessage( - "§a[更新] 已是" - + "最新版本"); - } - - // 显示另一通道的失败原因 - // (仅信息,不影响结果) - CheckResult other = - (src.equals("GitHub")) - ? ge : gh; - if (!other.isSuccess() - && other.error != null) { - sender.sendMessage( - "§7[更新] " - + other.source - + "不可用: " - + other.error); - } - }); - } - - - // ========== 禁用信号判断 ========== - - private boolean isDisableSignal( - String version) { - if (version == null) return false; - String v = version.trim().toLowerCase(); - return "999".equals(v) - || "关闭".equals(v) - || "false".equals(v) - || "禁用".equals(v); - } - - // ========== HTTP 请求(带超时) ========== - - // ========== 检测结果 ========== - - private static class CheckResult { - final String version; - final String source; - final String link; - final String error; - - CheckResult(String version, - String source, - String link, - String error) { - this.version = version; - this.source = source; - this.link = link; - this.error = error; - } - - boolean isSuccess() { - return version != null; - } - } - -// ========== HTTP 请求 ========== - - private CheckResult tryChannel( - String apiUrl, - String sourceName, - String link) { - HttpURLConnection conn = null; - try { - URL url = new URL(apiUrl); - conn = (HttpURLConnection) - url.openConnection(); - conn.setRequestMethod("GET"); - conn.setRequestProperty( - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; " - + "Win64; x64) AppleWebKit/537.36 " - + "(KHTML, like Gecko) " - + "Chrome/120.0.0.0 Safari/537.36"); - conn.setRequestProperty( - "Accept", - "*/*"); - conn.setRequestProperty( - "Accept-Language", - "zh-CN,zh;q=0.9"); - conn.setConnectTimeout(TIMEOUT_MS); - conn.setReadTimeout(TIMEOUT_MS); - conn.setInstanceFollowRedirects(true); - - int code = conn.getResponseCode(); - - if (code == 404) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": 未找到 (HTTP 404)"); - } - if (code == 403) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": 被限流 (HTTP 403)"); - } - if (code != 200) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": HTTP " + code); - } - - BufferedReader reader = - new BufferedReader( - new InputStreamReader( - conn - .getInputStream())); - StringBuilder sb = - new StringBuilder(); - String line; - while ((line = reader.readLine()) - != null) { - sb.append(line); - } - reader.close(); - - String tag = - parseTagName(sb.toString()); - if (tag == null) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": 无法解析版本号"); - } - - return new CheckResult(tag, - sourceName, link, null); - - } catch (java.net - .SocketTimeoutException e) { - return new CheckResult(null, - sourceName, link, - sourceName + ": 连接超时"); - } catch (java.net.ConnectException e) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": 连接被拒绝"); - } catch (java.net - .UnknownHostException e) { - return new CheckResult(null, - sourceName, link, - sourceName - + ": DNS解析失败"); - } catch (Exception e) { - return new CheckResult(null, - sourceName, link, - sourceName + ": " - + e.getMessage()); - } finally { - if (conn != null) { - conn.disconnect(); - } - } - } - - - - /** - * 从 JSON 中提取 tag_name - */ - private String parseTagName(String json) { - // tags API 返回数组: [{"name":"v1.1",...}] - // releases API 返回对象: {"tag_name":"v1.1"} - - // 尝试 tag_name(releases格式) - int idx = - json.indexOf("\"tag_name\""); - if (idx >= 0) { - int colon = - json.indexOf(":", idx); - int q1 = - json.indexOf("\"", colon + 1); - int q2 = - json.indexOf("\"", q1 + 1); - if (q2 > q1) { - return cleanTag( - json.substring( - q1 + 1, q2)); - } - } - - // 尝试 "name"(tags格式) - idx = json.indexOf("\"name\""); - if (idx >= 0) { - int colon = - json.indexOf(":", idx); - int q1 = - json.indexOf("\"", colon + 1); - int q2 = - json.indexOf("\"", q1 + 1); - if (q2 > q1) { - return cleanTag( - json.substring( - q1 + 1, q2)); - } - } - - return null; - } - - private String cleanTag(String tag) { - if (tag.startsWith("v") - || tag.startsWith("V")) { - tag = tag.substring(1); - } - return tag.isEmpty() ? null : tag; - } - - - // ========== 版本比较 ========== - - private boolean isNewer(String remote, - String local) { - String[] r = remote.split("\\."); - String[] l = local.split("\\."); - int len = Math.max(r.length, l.length); - for (int i = 0; i < len; i++) { - int ri = i < r.length - ? parseNum(r[i]) : 0; - int li = i < l.length - ? parseNum(l[i]) : 0; - if (ri > li) return true; - if (ri < li) return false; - } - return false; - } - - private int parseNum(String s) { - try { - return Integer.parseInt( - s.replaceAll("[^0-9]", "")); - } catch (Exception e) { - return 0; - } - } -} +package Sdf1_login; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +public class UpdateChecker { + + // ===== SSL证书信任所有(解决PKIX错误) ===== + static { + try { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + public void checkClientTrusted(X509Certificate[] certs, String authType) {} + public void checkServerTrusted(X509Certificate[] certs, String authType) {} + } + }; + SSLContext sc = SSLContext.getInstance("TLS"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + } catch (Exception ignored) {} + } + + private final Main plugin; + private final String currentVersion; + + // ===== 仓库信息 ===== + private static final String GITHUB_OWNER = "ypsdf1"; + private static final String GITHUB_REPO = "sdf1_login"; + private static final String GITEE_OWNER = "nihaoshidifu"; + private static final String GITEE_REPO = "sdf1_login"; + + // API 地址(使用releases/latest获取最新版本) + private static final String GITHUB_API = + "https://api.github.com/repos/" + GITHUB_OWNER + "/" + GITHUB_REPO + "/releases/latest"; + private static final String GITEE_API = + "https://gitee.com/api/v5/repos/" + GITEE_OWNER + "/" + GITEE_REPO + "/releases/latest"; + + // 仓库页面链接 + private static final String GITHUB_LINK = + "https://github.com/" + GITHUB_OWNER + "/" + GITHUB_REPO + "/releases"; + private static final String GITEE_LINK = + "https://gitee.com/" + GITEE_OWNER + "/" + GITEE_REPO + "/releases"; + + // 单次请求超时(毫秒) + private static final int TIMEOUT_MS = 5000; + + // 熔断阈值 + private static final int TRIP_THRESHOLD = 5; + + // 熔断后重试间隔(2分钟) + private static final long RETRY_DELAY_MS = 2 * 60 * 1000; + + // ===== 熔断状态(内存态,重启自动清除) ===== + private volatile boolean circuitTripped = false; + private volatile int failCount = 0; + private volatile boolean manualSuccess = false; + + // ===== 自动检测禁用(持久化到文件) ===== + private volatile boolean autoCheckDisabled = false; + private final File disabledFile; + + public UpdateChecker(Main plugin) { + this.plugin = plugin; + this.currentVersion = plugin.getDescription().getVersion(); + this.disabledFile = new File(plugin.getDataFolder(), "update_disabled.dat"); + loadDisabledState(); + } + + // ========== 持久化禁用状态 ========== + + private void loadDisabledState() { + if (disabledFile.exists()) { + try (BufferedReader br = new BufferedReader(new FileReader(disabledFile))) { + String line = br.readLine(); + if (line != null) { + autoCheckDisabled = "true".equals(line.trim()); + } + } catch (Exception ignored) { + } + } + } + + private void saveDisabledState() { + try (PrintWriter pw = new PrintWriter(new FileWriter(disabledFile))) { + pw.println(autoCheckDisabled ? "true" : "false"); + } catch (Exception ignored) { + } + } + + // ========== 公开方法 ========== + + /** + * onEnable 调用(异步静默) + * 熔断或禁用则直接跳过 + */ + public void checkOnEnable() { + if (autoCheckDisabled) return; + if (circuitTripped) { + // 熔断状态下,2分钟后重试 + scheduleRetry(); + return; + } + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> doCheck(null, true)); + } + + /** + * /sdf1_login update 调用(异步) + * 忽略熔断和禁用状态,强制检测 + */ + public void checkUpdate(CommandSender sender) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> doCheck(sender, false)); + } + + /** + * 2分钟后重试 + */ + private void scheduleRetry() { + Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (circuitTripped && !manualSuccess) { + plugin.getLogger().info("[Sdf1_login] 熔断重试中..."); + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> doCheck(null, true)); + } + }, RETRY_DELAY_MS / 50); // ticks + } + + // ========== 核心检测逻辑 ========== + private void doCheck(CommandSender sender, boolean isAuto) { + + if (isAuto) { + if (autoCheckDisabled || circuitTripped) { + return; + } + plugin.getLogger().info("[Sdf1_login] 启动自动检查更新..."); + } + + // ★ 双通道并行执行,一路成功掐断另一路 + AtomicBoolean cancelled = new AtomicBoolean(false); + AtomicReference ghResult = new AtomicReference<>(); + AtomicReference geResult = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(2); + + // 通道1:GitHub + Thread ghThread = new Thread(() -> { + if (!cancelled.get()) { + ghResult.set(tryChannel(GITHUB_API, "GitHub", GITHUB_LINK)); + if (ghResult.get().isSuccess()) { + cancelled.set(true); // 成功则取消另一路 + } + } + latch.countDown(); + }); + ghThread.setDaemon(true); + + // 通道2:Gitee + Thread geThread = new Thread(() -> { + if (!cancelled.get()) { + geResult.set(tryChannel(GITEE_API, "Gitee", GITEE_LINK)); + if (geResult.get().isSuccess()) { + cancelled.set(true); // 成功则取消另一路 + } + } + latch.countDown(); + }); + geThread.setDaemon(true); + + ghThread.start(); + geThread.start(); + + try { + latch.await(); // 等待两路都完成 + } catch (InterruptedException ignored) { + } + + CheckResult gh = ghResult.get(); + CheckResult ge = geResult.get(); + + // 选择成功的结果 + CheckResult chosen = null; + + if (gh != null && gh.isSuccess()) { + chosen = gh; + } else if (ge != null && ge.isSuccess()) { + chosen = ge; + } else { + // 双通道均失败 + failCount++; + StringBuilder sb = new StringBuilder(); + if (gh != null && gh.error != null) { + sb.append("§7GitHub: §c").append(gh.error); + } + if (ge != null && ge.error != null) { + if (sb.length() > 0) sb.append("\n"); + sb.append("§7Gitee: §c").append(ge.error); + } + if (failCount >= TRIP_THRESHOLD) { + circuitTripped = true; + plugin.getLogger().warning( + "[Sdf1_login] 更新检测已熔断,连续失败" + failCount + "次"); + // 熔断后安排2分钟后重试 + scheduleRetry(); + } + if (sender != null) { + final String msg = sb.toString(); + final boolean tripped = circuitTripped; + final int fc = failCount; + Bukkit.getScheduler().runTask(plugin, () -> { + sender.sendMessage("§c§l[更新] 检测失败"); + sender.sendMessage(msg); + sender.sendMessage("§e失败次数: " + fc + "/" + TRIP_THRESHOLD); + if (tripped) { + sender.sendMessage("§c已熔断,2分钟后自动重试,或重启服务器重置"); + } + }); + } + return; + } + + // 至少一个通道成功 + failCount = 0; + + // 检查禁用信号 + if (isDisableSignal(chosen.version)) { + autoCheckDisabled = true; + saveDisabledState(); + plugin.getLogger().info( + "[Sdf1_login] 远程版本信号 \"" + chosen.version + "\",自动更新检测已禁用"); + if (sender != null) { + Bukkit.getScheduler().runTask(plugin, () -> + sender.sendMessage("§e[更新] 远程已禁用自动更新检测")); + } + return; + } + + // 手动调用:成功才解除熔断和禁用 + if (!isAuto) { + manualSuccess = true; // 标记手动成功,不再执行后续自动检查 + if (circuitTripped) { + circuitTripped = false; + } + if (autoCheckDisabled) { + autoCheckDisabled = false; + saveDisabledState(); + } + } + + final String src = chosen.source; + final String lnk = chosen.link; + final String rv = chosen.version; + + // ★ 自动检查也记录日志(含下载链接 + 颜色提醒) + if (isAuto) { + boolean hasUpdate = isNewer(rv, currentVersion); + String status = hasUpdate + ? "(有新版本!)" + : "(已是最新)"; + // 使用Bukkit.getConsoleSender().sendMessage()输出彩色日志 + Bukkit.getConsoleSender().sendMessage("§a[Sdf1_login] §e更新检测成功 via: " + src + + " | 本地: " + currentVersion + " | 远程: " + rv + " §7" + status); + if (hasUpdate) { + Bukkit.getConsoleSender().sendMessage("§a[Sdf1_login] §b下载: " + lnk); + } + } + + Bukkit.getScheduler().runTask(plugin, () -> { + if (sender == null) return; + + sender.sendMessage("§e[Sdf1_login] [更新] 检测成功 via: §a" + src); + sender.sendMessage("§e[Sdf1_login] [更新] 本地版本: §f" + currentVersion); + sender.sendMessage("§e[Sdf1_login] [更新] 远程版本: §f" + rv); + + if (isNewer(rv, currentVersion)) { + sender.sendMessage("§c[Sdf1_login] [更新] 发现新版本: " + rv); + sender.sendMessage("§a[Sdf1_login] 请前往仓库下载更新:"); + sender.sendMessage("§a§l" + lnk); + } else { + sender.sendMessage("§a[Sdf1_login] [更新] 已是最新版本"); + } + + // 显示另一通道的失败原因 + CheckResult other = (src.equals("GitHub")) ? ge : gh; + if (other != null && !other.isSuccess() && other.error != null) { + sender.sendMessage("§7[Sdf1_login] [更新] " + other.source + " 不可用: " + other.error); + } + }); + } + + // ========== 禁用信号判断 ========== + + private boolean isDisableSignal(String version) { + if (version == null) return false; + String v = version.trim().toLowerCase(); + return "999".equals(v) || "关闭".equals(v) || "false".equals(v) || "禁用".equals(v); + } + + // ========== HTTP 请求 ========== + + private static class CheckResult { + final String version; + final String source; + final String link; + final String error; + + CheckResult(String version, String source, String link, String error) { + this.version = version; + this.source = source; + this.link = link; + this.error = error; + } + + boolean isSuccess() { + return version != null; + } + } + + private CheckResult tryChannel(String apiUrl, String sourceName, String link) { + HttpURLConnection conn = null; + try { + URL url = new URL(apiUrl); + conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("User-Agent", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"); + conn.setRequestProperty("Accept", "*/*"); + conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9"); + conn.setConnectTimeout(TIMEOUT_MS); + conn.setReadTimeout(TIMEOUT_MS); + conn.setInstanceFollowRedirects(true); + + int code = conn.getResponseCode(); + + if (code == 404) { + return new CheckResult(null, sourceName, link, sourceName + ": 未找到 (HTTP 404)"); + } + if (code == 403) { + return new CheckResult(null, sourceName, link, sourceName + ": 被限流 (HTTP 403)"); + } + if (code != 200) { + return new CheckResult(null, sourceName, link, sourceName + ": HTTP " + code); + } + + BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + reader.close(); + + String tag = parseTagName(sb.toString()); + if (tag == null) { + return new CheckResult(null, sourceName, link, sourceName + ": 无法解析版本号"); + } + + return new CheckResult(tag, sourceName, link, null); + + } catch (java.net.SocketTimeoutException e) { + return new CheckResult(null, sourceName, link, sourceName + ": 连接超时"); + } catch (java.net.ConnectException e) { + return new CheckResult(null, sourceName, link, sourceName + ": 连接被拒绝"); + } catch (java.net.UnknownHostException e) { + return new CheckResult(null, sourceName, link, sourceName + ": DNS解析失败"); + } catch (Exception e) { + return new CheckResult(null, sourceName, link, sourceName + ": " + e.getMessage()); + } finally { + if (conn != null) { + conn.disconnect(); + } + } + } + + private String parseTagName(String json) { + // tags API 返回数组: [{"name":"v1.1",...}] + // releases API 返回对象: {"tag_name":"v1.1"} + + int idx = json.indexOf("\"tag_name\""); + if (idx >= 0) { + int colon = json.indexOf(":", idx); + int q1 = json.indexOf("\"", colon + 1); + int q2 = json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag(json.substring(q1 + 1, q2)); + } + } + + idx = json.indexOf("\"name\""); + if (idx >= 0) { + int colon = json.indexOf(":", idx); + int q1 = json.indexOf("\"", colon + 1); + int q2 = json.indexOf("\"", q1 + 1); + if (q2 > q1) { + return cleanTag(json.substring(q1 + 1, q2)); + } + } + + return null; + } + + private String cleanTag(String tag) { + if (tag.startsWith("v") || tag.startsWith("V")) { + tag = tag.substring(1); + } + return tag.isEmpty() ? null : tag; + } + + // ========== 版本比较 ========== + // ★ 只要本地≠远程,就视为有更新 + + private boolean isNewer(String remote, String local) { + if (remote == null || local == null) return false; + // 版本比较:远程≠本地即视为有更新(用户即将推新版本) + return !remote.trim().equals(local.trim()); + } + + private int parseNum(String s) { + try { + return Integer.parseInt(s.replaceAll("[^0-9]", "")); + } catch (Exception e) { + return 0; + } + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index f6868d9..62a4cc6 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: Sdf1_login -version: 1.0 +version: 2.1 main: Sdf1_login.Main api-version: '1.21' commands: @@ -101,4 +101,7 @@ commands: # usage: /testorder protect: description: 区域保护 - usage: /protect \ No newline at end of file + usage: /protect + update: + description: 检查所有插件更新(集群控制) + usage: /update \ No newline at end of file

    tclrUkJU2ClxHyuZfV+Wb+|2{n@ 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 RV(9tDDLhMv_Nq!?oyl-DDLil^L^)>`>vUl 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 Tsp6m;9IUA-X(=|!zYqI=LgcSd literal 0 HcmV?d00001 diff --git a/backup/img/shop6.png b/backup/img/shop6.png new file mode 100644 index 0000000000000000000000000000000000000000..e69fedd4fc811c7b755aea5d00521aec3b905d10 GIT binary patch literal 230212 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 z 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 z⪙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