164 Commits
1.0.7 ... 1.1.3

Author SHA1 Message Date
d2fd8c496d Upload readme.md via GUI 2026-02-05 21:15:10 +00:00
1ca544fadc Upload pom.xml via GUI 2026-02-05 21:15:09 +00:00
253515862e Update from Git Manager GUI 2026-02-05 22:15:08 +01:00
8693ef41af readme.md aktualisiert 2026-01-22 20:09:44 +00:00
52f00f9b93 readme.md aktualisiert 2026-01-22 20:09:27 +00:00
fb05868e65 readme.md aktualisiert 2026-01-22 20:09:02 +00:00
6943022cd3 readme.md aktualisiert 2026-01-22 20:08:32 +00:00
da5adc8034 readme.md aktualisiert 2026-01-22 20:05:13 +00:00
197acc6750 readme.md aktualisiert 2026-01-22 20:00:13 +00:00
40295f3711 Upload readme.md via GUI 2026-01-13 20:06:41 +00:00
1638ad4e8a Upload pom.xml via GUI 2026-01-13 20:06:41 +00:00
e5251933e1 Update from Git Manager GUI 2026-01-13 21:06:39 +01:00
08b05167d8 readme.md aktualisiert 2026-01-13 17:58:57 +00:00
58927afddc readme.md aktualisiert 2026-01-13 13:14:22 +00:00
4765f652a8 readme.md aktualisiert 2026-01-13 13:09:54 +00:00
6fed70c831 target/classes/de/viper/survivalplus/commands/AnvilCommand.class gelöscht 2026-01-13 13:09:37 +00:00
97f739a302 target/classes/de/viper/survivalplus/commands/BackCommand.class gelöscht 2026-01-13 13:09:33 +00:00
fef02a2657 target/classes/de/viper/survivalplus/commands/BlockListCommand.class gelöscht 2026-01-13 13:09:28 +00:00
b06008069a target/classes/de/viper/survivalplus/commands/ClaimCommand.class gelöscht 2026-01-13 13:09:23 +00:00
c0d2125176 target/classes/de/viper/survivalplus/commands/WarpsCommand.class gelöscht 2026-01-13 13:09:18 +00:00
6f7d5c3872 target/classes/de/viper/survivalplus/commands/WorkbenchCommand.class gelöscht 2026-01-13 13:09:14 +00:00
2d4a253d1b target/classes/de/viper/survivalplus/commands/TradeCommand.class gelöscht 2026-01-13 13:09:10 +00:00
aaea889832 target/classes/de/viper/survivalplus/commands/UnblockCommand.class gelöscht 2026-01-13 13:09:06 +00:00
653abaaac4 target/classes/de/viper/survivalplus/commands/TrashCommand.class gelöscht 2026-01-13 13:09:03 +00:00
fd8122beff target/classes/de/viper/survivalplus/commands/TradeAcceptCommand.class gelöscht 2026-01-13 13:08:59 +00:00
e4ece02a00 target/classes/de/viper/survivalplus/commands/StartFunChallengeCommand.class gelöscht 2026-01-13 13:08:54 +00:00
21a92bad90 target/classes/de/viper/survivalplus/commands/TeleportCommands.class gelöscht 2026-01-13 13:08:50 +00:00
326eed3513 target/classes/de/viper/survivalplus/commands/StatsCommand.class gelöscht 2026-01-13 13:08:46 +00:00
7b1a844234 target/classes/de/viper/survivalplus/commands/SitCommand.class gelöscht 2026-01-13 13:08:42 +00:00
afe6438ec2 target/classes/de/viper/survivalplus/commands/SpawnCommand.class gelöscht 2026-01-13 13:08:37 +00:00
484f08c852 target/classes/de/viper/survivalplus/commands/ShowReportCommand.class gelöscht 2026-01-13 13:08:34 +00:00
9f19a94d27 target/classes/de/viper/survivalplus/commands/HealCommand.class gelöscht 2026-01-13 13:08:29 +00:00
aeac179cec target/classes/de/viper/survivalplus/commands/ShowArmorStandsCommand.class gelöscht 2026-01-13 13:08:25 +00:00
f10f6eebc8 target/classes/de/viper/survivalplus/commands/ShopCommand.class gelöscht 2026-01-13 13:08:20 +00:00
5b984d1e7d target/classes/de/viper/survivalplus/commands/SetWarpCommand.class gelöscht 2026-01-13 13:08:15 +00:00
68eddb1277 target/classes/de/viper/survivalplus/commands/RideCommand.class gelöscht 2026-01-13 13:08:12 +00:00
bf18ab3888 target/classes/de/viper/survivalplus/commands/SetSpawnCommand.class gelöscht 2026-01-13 13:08:08 +00:00
5ffc7a716b target/classes/de/viper/survivalplus/commands/SetWorldSpawnCommand.class gelöscht 2026-01-13 13:08:04 +00:00
b8c4ce5dd9 target/classes/de/viper/survivalplus/commands/ReportCommand.class gelöscht 2026-01-13 13:07:54 +00:00
f41f935ad3 target/classes/de/viper/survivalplus/commands/PluginCommand.class gelöscht 2026-01-13 13:07:51 +00:00
7521ec5541 target/classes/de/viper/survivalplus/commands/NightCommand.class gelöscht 2026-01-13 13:07:47 +00:00
919b7a2cf5 target/classes/de/viper/survivalplus/commands/NickCommand.class gelöscht 2026-01-13 13:07:43 +00:00
fb22f49f3d target/classes/de/viper/survivalplus/commands/LeashCountCommand.class gelöscht 2026-01-13 13:07:39 +00:00
9a654ec6bf target/classes/de/viper/survivalplus/commands/KitCommand.class gelöscht 2026-01-13 13:07:35 +00:00
810c301daa target/classes/de/viper/survivalplus/commands/ItemRenameCommand.class gelöscht 2026-01-13 13:07:30 +00:00
92b5a3725c target/classes/de/viper/survivalplus/commands/InventoryCommand$SnapshotHolder.class gelöscht 2026-01-13 13:07:26 +00:00
3541a7749d target/classes/de/viper/survivalplus/commands/InventoryCommand.class gelöscht 2026-01-13 13:07:22 +00:00
a02d322201 target/classes/de/viper/survivalplus/commands/HomeCommand.class gelöscht 2026-01-13 13:07:18 +00:00
a0d8fc8f6b target/classes/de/viper/survivalplus/commands/DayCommand.class gelöscht 2026-01-13 13:07:14 +00:00
80a6c0ec14 target/classes/de/viper/survivalplus/commands/GamemodeCommand.class gelöscht 2026-01-13 13:07:09 +00:00
f36e5d8b4e target/classes/de/viper/survivalplus/commands/FriendCommand.class gelöscht 2026-01-13 13:07:05 +00:00
e5e661dcac target/classes/de/viper/survivalplus/commands/EnderchestCommand.class gelöscht 2026-01-13 13:07:01 +00:00
c23aedb4e5 target/classes/de/viper/survivalplus/commands/DelWarpCommand.class gelöscht 2026-01-13 13:06:57 +00:00
08569d98c6 target/classes/de/viper/survivalplus/commands/CloseDoorsCommand.class gelöscht 2026-01-13 13:06:53 +00:00
406b28d572 target/classes/de/viper/survivalplus/commands/ClearDebugArmorStandsCommand.class gelöscht 2026-01-13 13:06:49 +00:00
186c9d1841 target/classes/de/viper/survivalplus/commands/ClearReportCommand.class gelöscht 2026-01-13 13:06:44 +00:00
3b535b15ca target/classes/de/viper/survivalplus/commands/ClearItemsCommand.class gelöscht 2026-01-13 13:06:41 +00:00
a8c7892f54 target/classes/de/viper/survivalplus/commands/ClearChatCommand.class gelöscht 2026-01-13 13:06:37 +00:00
44d0818988 target/classes/de/viper/survivalplus/commands/BlockCommand.class gelöscht 2026-01-13 13:06:33 +00:00
c1fd0477b1 target/classes/de/viper/survivalplus/fun/FunChallengeManager.class gelöscht 2026-01-13 13:06:28 +00:00
531a8d4206 target/classes/de/viper/survivalplus/fun/FunChallenge.class gelöscht 2026-01-13 13:06:24 +00:00
6e544e34e9 target/classes/de/viper/survivalplus/fun/FunChallengeManager$1.class gelöscht 2026-01-13 13:06:20 +00:00
dfb785cb09 target/classes/de/viper/survivalplus/gui/ShopGui.class gelöscht 2026-01-13 13:06:15 +00:00
78f4c75c94 target/classes/de/viper/survivalplus/listeners/AFKListener$1.class gelöscht 2026-01-13 13:06:08 +00:00
c10e0a6645 target/classes/de/viper/survivalplus/listeners/ArmorStandDestroyListener.class gelöscht 2026-01-13 13:06:02 +00:00
1717a8c84f target/classes/de/viper/survivalplus/listeners/BlockDetectionListener.class gelöscht 2026-01-13 13:05:57 +00:00
e4b5bc6490 target/classes/de/viper/survivalplus/listeners/ChallengeCollectListener.class gelöscht 2026-01-13 13:05:53 +00:00
bb6e876932 target/classes/de/viper/survivalplus/listeners/StatsListener.class gelöscht 2026-01-13 13:05:49 +00:00
f2a401f684 target/classes/de/viper/survivalplus/listeners/WarpInventoryListener.class gelöscht 2026-01-13 13:05:45 +00:00
d27a4221ef target/classes/de/viper/survivalplus/listeners/ToolUpgradeListener$1.class gelöscht 2026-01-13 13:05:41 +00:00
16337cbf18 target/classes/de/viper/survivalplus/listeners/ToolUpgradeListener.class gelöscht 2026-01-13 13:05:37 +00:00
0b7f32e850 target/classes/de/viper/survivalplus/listeners/OreAlarmListener$1.class gelöscht 2026-01-13 13:05:33 +00:00
3f5cc88b30 target/classes/de/viper/survivalplus/listeners/SpawnProtectionListener.class gelöscht 2026-01-13 13:05:27 +00:00
7b7bf9c72e target/classes/de/viper/survivalplus/listeners/SleepListener.class gelöscht 2026-01-13 13:05:23 +00:00
948e018cff target/classes/de/viper/survivalplus/listeners/SitListener.class gelöscht 2026-01-13 13:05:20 +00:00
54bee6a633 target/classes/de/viper/survivalplus/listeners/SignColorListener.class gelöscht 2026-01-13 13:05:15 +00:00
2428a35744 target/classes/de/viper/survivalplus/listeners/RepairSignListener$1.class gelöscht 2026-01-13 13:05:12 +00:00
b844a7aaea target/classes/de/viper/survivalplus/listeners/RepairSignListener.class gelöscht 2026-01-13 13:05:08 +00:00
aa4360a7cf target/classes/de/viper/survivalplus/listeners/PlayerJoinListener.class gelöscht 2026-01-13 13:05:04 +00:00
bab76e0a93 target/classes/de/viper/survivalplus/listeners/OreAlarmListener$OreData.class gelöscht 2026-01-13 13:05:00 +00:00
fa4d08b018 target/classes/de/viper/survivalplus/listeners/MobCapListener.class gelöscht 2026-01-13 13:04:56 +00:00
075667a9e3 target/classes/de/viper/survivalplus/listeners/NickJoinMessageListener.class gelöscht 2026-01-13 13:04:51 +00:00
40ba636c3b target/classes/de/viper/survivalplus/listeners/OreAlarmListener.class gelöscht 2026-01-13 13:04:43 +00:00
f15d97e2af target/classes/de/viper/survivalplus/listeners/NickLoadListener.class gelöscht 2026-01-13 13:04:36 +00:00
d81e2c28c9 target/classes/de/viper/survivalplus/listeners/NewbieProtectionListener$1.class gelöscht 2026-01-13 13:04:30 +00:00
1c66acbd5c target/classes/de/viper/survivalplus/listeners/NewbieProtectionListener.class gelöscht 2026-01-13 13:04:26 +00:00
9b50de8f1f target/classes/de/viper/survivalplus/listeners/MobLeashLimitListener.class gelöscht 2026-01-13 13:04:22 +00:00
b2242c49e1 target/classes/de/viper/survivalplus/listeners/LoginListener.class gelöscht 2026-01-13 13:04:18 +00:00
973997cc47 target/classes/de/viper/survivalplus/listeners/ChallengeSmeltListener.class gelöscht 2026-01-13 13:04:14 +00:00
eca466ef1a target/classes/de/viper/survivalplus/listeners/GraveListener$1.class gelöscht 2026-01-13 13:04:09 +00:00
d9b23915e8 target/classes/de/viper/survivalplus/listeners/InventoryClickListener.class gelöscht 2026-01-13 13:04:06 +00:00
702b51be1d target/classes/de/viper/survivalplus/listeners/GraveListener$2.class gelöscht 2026-01-13 13:04:02 +00:00
b88d599b3b target/classes/de/viper/survivalplus/listeners/GraveListener.class gelöscht 2026-01-13 13:03:58 +00:00
a8c2b58b86 target/classes/de/viper/survivalplus/listeners/FirstJoinListener.class gelöscht 2026-01-13 13:03:53 +00:00
55a54f857a target/classes/de/viper/survivalplus/listeners/DebugArmorStandListener.class gelöscht 2026-01-13 13:03:49 +00:00
6b0431f230 target/classes/de/viper/survivalplus/listeners/ClaimListener.class gelöscht 2026-01-13 13:03:45 +00:00
78b03f743b target/classes/de/viper/survivalplus/listeners/ChatBlockListener.class gelöscht 2026-01-13 13:03:41 +00:00
7b70bdcaef target/classes/de/viper/survivalplus/listeners/BackpackListener.class gelöscht 2026-01-13 13:03:37 +00:00
05e9cd1e24 target/classes/de/viper/survivalplus/listeners/AFKListener.class gelöscht 2026-01-13 13:03:33 +00:00
2cf634d7a6 target/classes/de/viper/survivalplus/listeners/AdaptiveMobListener.class gelöscht 2026-01-13 13:03:29 +00:00
d397ae25a8 target/classes/de/viper/survivalplus/Manager/BlockManager.class gelöscht 2026-01-13 13:03:21 +00:00
188b1618da target/classes/de/viper/survivalplus/Manager/CommandBlocker.class gelöscht 2026-01-13 13:03:17 +00:00
4ba7109e92 target/classes/de/viper/survivalplus/Manager/LootChestManager.class gelöscht 2026-01-13 13:03:14 +00:00
b7ed8b3515 target/classes/de/viper/survivalplus/Manager/LootChestManager$1.class gelöscht 2026-01-13 13:03:10 +00:00
875f7e1650 target/classes/de/viper/survivalplus/Manager/ShopManager.class gelöscht 2026-01-13 13:03:06 +00:00
f3db9b5d8b target/classes/de/viper/survivalplus/Manager/LootChestManager$3.class gelöscht 2026-01-13 13:03:03 +00:00
2179a73329 target/classes/de/viper/survivalplus/Manager/LootChestManager$LootItem.class gelöscht 2026-01-13 13:02:57 +00:00
526d9b00a6 target/classes/de/viper/survivalplus/Manager/LootChestManager$2.class gelöscht 2026-01-13 13:02:51 +00:00
242f54b3dc target/classes/de/viper/survivalplus/Manager/StatsManager.class gelöscht 2026-01-13 13:02:47 +00:00
a8c1d2fb10 target/classes/de/viper/survivalplus/Manager/TablistManager.class gelöscht 2026-01-13 13:02:41 +00:00
720b54f4e1 target/classes/de/viper/survivalplus/Manager/TablistManager$1.class gelöscht 2026-01-13 13:02:37 +00:00
8be068ab72 target/classes/de/viper/survivalplus/Manager/Warp.class gelöscht 2026-01-13 13:02:33 +00:00
d2ef092625 target/classes/de/viper/survivalplus/Manager/WarpManager.class gelöscht 2026-01-13 13:02:29 +00:00
31b6772807 target/classes/de/viper/survivalplus/recipe/BackpackRecipe.class gelöscht 2026-01-13 13:02:21 +00:00
fbd9c65f9f target/classes/de/viper/survivalplus/report/ReportManager.class gelöscht 2026-01-13 13:02:16 +00:00
9329f8ec25 target/classes/de/viper/survivalplus/tasks/AFKManager.class gelöscht 2026-01-13 13:02:10 +00:00
ebe50e9b07 target/classes/de/viper/survivalplus/tasks/AFKManager$1.class gelöscht 2026-01-13 13:02:06 +00:00
4e34c30c08 target/classes/de/viper/survivalplus/tasks/AutoClearTask.class gelöscht 2026-01-13 13:02:03 +00:00
2d088fa663 target/classes/de/viper/survivalplus/trade/TradeManager.class gelöscht 2026-01-13 13:01:57 +00:00
a4927efbd4 target/classes/de/viper/survivalplus/trade/TradeSession.class gelöscht 2026-01-13 13:01:53 +00:00
b8efc67e49 target/classes/de/viper/survivalplus/util/BannerManager.class gelöscht 2026-01-13 13:01:45 +00:00
62b7092bcb target/classes/de/viper/survivalplus/util/Claim.class gelöscht 2026-01-13 13:01:41 +00:00
a95145f9fa target/classes/de/viper/survivalplus/util/LockSystem.class gelöscht 2026-01-13 13:01:38 +00:00
1b0fc748e5 target/classes/de/viper/survivalplus/util/LockSystem$1.class gelöscht 2026-01-13 13:01:34 +00:00
f035d34e2c target/classes/de/viper/survivalplus/util/LockSystem$2.class gelöscht 2026-01-13 13:01:31 +00:00
f2938a516e target/classes/de/viper/survivalplus/util/LockSystem$LockData.class gelöscht 2026-01-13 13:01:26 +00:00
389ad08e69 target/classes/de/viper/survivalplus/SurvivalPlus.class gelöscht 2026-01-13 13:01:18 +00:00
f6d91971f0 target/classes/de/viper/survivalplus/ForceSurvivalListener.class gelöscht 2026-01-13 13:01:14 +00:00
a0bd8f1606 target/classes/de/viper/survivalplus/SurvivalPlus$1.class gelöscht 2026-01-13 13:01:10 +00:00
f28adb0be6 target/classes/blockedcommands.yml gelöscht 2026-01-13 13:01:02 +00:00
c593c97c03 target/classes/claims.yml gelöscht 2026-01-13 13:00:58 +00:00
66dcd21966 target/classes/config.yml gelöscht 2026-01-13 13:00:54 +00:00
a7d4e14c09 target/classes/shop.yml gelöscht 2026-01-13 13:00:50 +00:00
9967dc5417 target/classes/lang.yml gelöscht 2026-01-13 13:00:46 +00:00
dbeb24e676 target/classes/plugin.yml gelöscht 2026-01-13 13:00:43 +00:00
c495513c71 target/classes/leashes.yml gelöscht 2026-01-13 13:00:39 +00:00
230412e658 target/classes/help.yml gelöscht 2026-01-13 13:00:34 +00:00
8c80824830 target/classes/homes.yml gelöscht 2026-01-13 13:00:30 +00:00
db7c8eeaa5 target/classes/graves.yml gelöscht 2026-01-13 13:00:26 +00:00
53b79311f4 target/classes/stats.yml gelöscht 2026-01-13 13:00:21 +00:00
242c91be72 target/classes/tablist.yml gelöscht 2026-01-13 13:00:17 +00:00
fa33cfe1ba target/classes/warps.yml gelöscht 2026-01-13 13:00:13 +00:00
1173bfa49d target/maven-archiver/pom.properties gelöscht 2026-01-13 13:00:06 +00:00
4f9268032d target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst gelöscht 2026-01-13 13:00:00 +00:00
3a03b160ea target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst gelöscht 2026-01-13 12:59:56 +00:00
0a81b07fe6 target/original-SurvivalPlus-1.1.1-Beta.jar gelöscht 2026-01-13 12:59:49 +00:00
30d7249c2b target/SurvivalPlus-1.1.1-Beta.jar gelöscht 2026-01-13 12:59:45 +00:00
b10c0a9d40 Upload readme.md via GUI 2026-01-13 12:59:24 +00:00
7e2b4c5f38 Upload pom.xml via GUI 2026-01-13 12:59:23 +00:00
9818bc1229 Update from Git Manager GUI 2026-01-13 13:59:22 +01:00
e7f4c3ac5d Update from Git Manager GUI 2026-01-13 13:59:19 +01:00
6b6d3bc6b7 Update from Git Manager GUI 2026-01-13 13:59:15 +01:00
222186a83a .gitignore gelöscht 2026-01-11 23:03:50 +00:00
fbf9f10b27 Upload pom.xml via GUI 2026-01-11 23:03:16 +00:00
25339444da Update from Git Manager GUI 2026-01-12 00:03:14 +01:00
3f3d1c7d37 readme.md aktualisiert 2026-01-11 21:36:30 +00:00
4e2e831a1a readme.md aktualisiert 2026-01-11 21:35:16 +00:00
fec84fd8d7 src/main/java/de/viper/survivalplus/listeners/ToolUpgradeListener.java aktualisiert 2025-09-28 09:00:05 +00:00
39cf666bf4 readme.md aktualisiert 2025-08-27 18:58:51 +00:00
24c551fb5c readme.md aktualisiert 2025-08-27 18:48:33 +00:00
790980a5ac readme.md aktualisiert 2025-08-27 18:24:02 +00:00
779f031fdc readme.md aktualisiert 2025-08-27 18:18:29 +00:00
7fb9afa9d3 Update: Versionierung in config/lang/help/tablist.yml + Debug-System 2025-08-27 20:07:30 +02:00
a69200ee17 src/main/resources/plugin.yml aktualisiert 2025-08-25 13:25:51 +00:00
48 changed files with 5163 additions and 1352 deletions

5
.gitignore vendored
View File

@@ -1,5 +0,0 @@
# Ignoriere Build-Ordner
target/
# Ignoriere ZIP-Dateien
*.zip

BIN
lib/ProtocolLib.jar Normal file

Binary file not shown.

48
pom.xml
View File

@@ -6,15 +6,13 @@
<groupId>de.viper</groupId> <groupId>de.viper</groupId>
<artifactId>SurvivalPlus</artifactId> <artifactId>SurvivalPlus</artifactId>
<version>1.0.7-Beta</version> <version>1.1.3</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>SurvivalPlus</name> <name>SurvivalPlus</name>
<properties> <properties>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@@ -23,20 +21,25 @@
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository> </repository>
<!-- Repository für PlaceholderAPI -->
<repository> <repository>
<id>codemc-repo</id> <id>codemc-repo</id>
<url>https://repo.codemc.io/repository/maven-public/</url> <url>https://repo.codemc.io/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<!-- Spigot API -->
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version> <version>1.21-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- bStats Bukkit Dependency --> <!-- bStats Bukkit Dependency -->
<dependency> <dependency>
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>
@@ -44,6 +47,7 @@
<version>3.0.2</version> <version>3.0.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- PlaceholderAPI Dependency --> <!-- PlaceholderAPI Dependency -->
<dependency> <dependency>
<groupId>me.clip</groupId> <groupId>me.clip</groupId>
@@ -51,6 +55,7 @@
<version>2.11.6</version> <version>2.11.6</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- LuckPerms API Dependency --> <!-- LuckPerms API Dependency -->
<dependency> <dependency>
<groupId>net.luckperms</groupId> <groupId>net.luckperms</groupId>
@@ -58,23 +63,42 @@
<version>5.5.10</version> <version>5.5.10</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- ProtocolLib 4.8.0 -->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.8.0</version>
<scope>provided</scope>
</dependency>
<!-- Vault API (JitPack) -->
<dependency>
<groupId>com.github.MilkBowl</groupId>
<artifactId>VaultAPI</artifactId>
<version>1.7.1</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>
<!-- Compiler Plugin -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>17</source> <!-- Hier KEINE expliziten source/target Werte setzen. -->
<target>17</target> <!-- Maven nutzt jetzt automatisch die 21 aus den Properties oben. -->
</configuration> </configuration>
</plugin> </plugin>
<!-- Shade Plugin -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version> <version>3.5.0</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@@ -113,6 +137,12 @@
<include>warps.yml</include> <include>warps.yml</include>
<include>tablist.yml</include> <include>tablist.yml</include>
<include>blockedcommands.yml</include> <include>blockedcommands.yml</include>
<include>claims.yml</include>
<include>lootchests.yml</include>
<include>blocks.yml</include>
<include>nicknames.yml</include>
<include>mobadapt.yml</include>
<include>heads.yml</include>
</includes> </includes>
</resource> </resource>
</resources> </resources>

418
readme.md
View File

@@ -1,106 +1,354 @@
# SurvivalPlus # SurvivalPlus
![Minecraft Plugin](https://img.shields.io/badge/Minecraft-Plugin-green) **Minecraft Plugin für 1.21+**
![Version](https://img.shields.io/badge/version-1.0.5-blue)
![Author](https://img.shields.io/badge/author-M_Viper-yellow)
**SurvivalPlus** ist ein Minecraft-Plugin zur Verbesserung des Survival-Erlebnisses. **Version:** 1.1.3
Es bietet Homes, Teleportation, Inventarverwaltung, Freundeslisten, Shops, Loot-Kisten und weitere Komfortbefehle. **Autor:** M_Viper
--- ---
## Features ## 📦 Übersicht & Features
- **Homes & Warps** Homepunkte setzen, löschen, Teleportation, persönliche Warps **SurvivalPlus** erweitert das klassische Minecraft-Survival mit Komfort, Schutz und Admin-Tools:
- **Teleportation & Spielerinteraktion** `/tp`, `/tphere`, `/tpa`, `/back`, `/spawn`
- **Inventar & Endertruhe** Öffnen eigener und fremder Inventare/Endertruhen - Homes, Warps, Teleportsystem (Tpa, Back, Spawn)
- **Spielmodus & Zeit** `/gm`, `/day`, `/night` - Claim-System (Anti-Grief, Trust, Ban, Kick, Claim-Infos)
- **Freundes- und Kommunikationssystem** `/friend`, `/block`, `/blocklist` - Inventar- & Enderchest-Management (auch für Admins)
- **Koordinaten teilen** `/sp share`, `/sp shareconfirm`, `/sp sharecancel` - Freundesliste, Block- & Report-System
- **Items & Werkzeuge** `/ir`, `/workbench`, `/anvil`, `/kit` - Shop-System, Lootchests, Sign Shops (Vault)
- **Server-Management** `/clearchat`, `/clearitems`, `/closedoors`, `/lock`, `/shop` - Player Heads, Vanish, Freeze, Ride, Sitzfunktion
- **Statistiken & Reporting** `/stats`, `/report`, `/showreport`, `/clearreport` - CommandBlocker, Clearchat, Clearitems, Closedoors, Lock-System
- **Fun & Challenges** `/sit`, `/startchallenge`, `/trade`, `/lootchests` - Animierte Tablist, bStats, Debug-Logging, umfangreiche Konfiguration
--- ---
## 🛠 Commands Übersicht ## 🛠 Befehle (Commands)
### Teleportation Alle Befehle und Permissions findest du auch in der **plugin.yml**. Hier die wichtigsten:
| Befehl | Nutzung | Permission |
|--------|---------|------------|
| `/tp` | Teleport zu Spieler | `survivalplus.tp` |
| `/tphere` | Spieler zu dir teleportieren | `survivalplus.tphere` |
| `/tpa` | Teleportanfrage senden | `survivalplus.tpa` |
| `/tpaccept` | Teleportanfrage annehmen | `survivalplus.tpaccept` |
| `/tpdeny` | Teleportanfrage ablehnen | `survivalplus.tpdeny` |
| `/back` | Zum letzten Todespunkt | `survivalplus.back` |
| `/spawn` | Weltspawn teleport | `survivalplus.spawn` |
### Homes & Warps | Befehl | Beschreibung | Permission |
| Befehl | Nutzung | Permission | |--------|--------------|------------|
|--------|---------|------------| | /sp | Hauptbefehl (Info, Reload, Help, Share, CommandBlocker) | survivalplus.sp |
| `/sethome <name>` | Home setzen | `survivalplus.homes.set` | | /help | Hilfe & Übersicht | survivalplus.sp |
| `/delhome <name>` | Home löschen | `survivalplus.homes.delete` | | /ir <Name> | Item umbenennen | survivalplus.itemrename |
| `/home <name>` | Teleport zu Home | `survivalplus.homes` | | /workbench | Werkbank-GUI öffnen | survivalplus.workbench |
| `/homelist` | Liste aller Homes | `survivalplus.homes.list` | | /anvil | Amboss-GUI öffnen | survivalplus.anvil |
| `/setwarp <name>` | Persönlichen Warp setzen | `survivalplus.setwarp` | | /trash | Mülleimer öffnen | survivalplus.trash |
| `/delwarp <name>` | Persönlichen Warp löschen | `survivalplus.delwarp` | | /showarmorstands | Debug: ArmorStands sichtbar | survivalplus.showarmorstands |
| `/warps` | GUI aller Warps öffnen | `survivalplus.warps` | | /cleardebugarmorstands | Debug-ArmorStands entfernen | survivalplus.cleardebugarmorstands |
| /leashcount | Geleinte Tiere anzeigen | survivalplus.leashcount |
### Spieler & Kommunikation | /nick <Name> | Nickname setzen | survivalplus.nick |
| Befehl | Nutzung | Permission | | /head <Spieler> | Spieler-Kopf erhalten | survivalplus.head |
|--------|---------|------------| | /vanish | Unsichtbar werden | survivalplus.vanish |
| `/friend` | Freundesliste verwalten | `survivalplus.friend` | | /freeze <Spieler> | Spieler einfrieren | survivalplus.freeze |
| `/block` | Spieler blockieren | `survivalplus.block` | | /ride [Spieler] | Reite einen Spieler | survivalplus.ride |
| `/unblock` | Blockierung aufheben | `survivalplus.unlock` | | /tp <Spieler> | Teleport zu Spieler | survivalplus.tp |
| `/blocklist` | Liste blockierter Spieler | `survivalplus.blocklist` | | /tphere <Spieler> | Spieler zu dir teleportieren | survivalplus.tphere |
| /tpa <Spieler> | Teleportanfrage senden | survivalplus.tpa |
### Items & Werkzeuge | /tpaccept | Anfrage akzeptieren | survivalplus.tpaccept |
| Befehl | Nutzung | Permission | | /tpdeny | Anfrage ablehnen | survivalplus.tpdeny |
|--------|---------|------------| | /back | Zum letzten Todespunkt | survivalplus.back |
| `/ir <neuer_name>` | Item umbenennen | `survivalplus.itemrename` | | /spawn | Zum Weltspawn | survivalplus.spawn |
| `/workbench` | Werkbank GUI öffnen | `survivalplus.workbench` | | /setspawn | Server-Spawn setzen | survivalplus.setspawn |
| `/anvil` | Amboss GUI öffnen | `survivalplus.anvil` | | /setworldspawn | Welt-Spawn setzen | survivalplus.setworldspawn |
| `/kit` | Starterkit erhalten | `survivalplus.kit` | | /sethome <Name> | Home setzen | survivalplus.homes.set |
| `/leashcount` | Anzahl geleinter Tiere | `survivalplus.leashcount` | | /delhome <Name> | Home löschen | survivalplus.homes.delete |
| `/nick <Name>` | Nickname ändern | `survivalplus.nick` | | /home <Name> | Zu Home teleportieren | survivalplus.homes |
| /homelist | GUI mit allen Homes | survivalplus.homes.list |
### Server-Management | /setwarp <Name> | Persönlichen Warp setzen | survivalplus.setwarp |
| Befehl | Nutzung | Permission | | /delwarp <Name> | Warp löschen | survivalplus.delwarp |
|--------|---------|------------| | /warps | Warp-GUI öffnen | survivalplus.warps |
| `/clearchat` | Chat löschen | `survivalplus.clearchat` | | /claim ... | Claim-System verwalten | survivalplus.claim.* |
| `/clearitems` | Items entfernen | `survivalplus.clearitems` | | /sp cb ... | CommandBlocker verwalten | survivalplus.commandblocker.* |
| `/closedoors <radius>` | Türen schließen | `survivalplus.closedoors` | | /clearchat | Chat leeren | survivalplus.clearchat |
| `/lock` | Container schützen | `survivalplus.lock` | | /clearitems | Items entfernen | survivalplus.clearitems |
| `/shop add <item> <preis> <bestand>` | Server-Shop verwalten | `survivalplus.shop` | | /closedoors <radius> | Türen schließen | survivalplus.closedoors |
| /sp lock | Kisten/Türen sperren | survivalplus.lock |
### Statistiken & Reporting | /shop ... | Shop verwalten | survivalplus.shop |
| Befehl | Nutzung | Permission | | /lootchests | Loot-Kisten auflisten | survivalplus.lootchests |
|--------|---------|------------| | /tploot <welt> <x> <y> <z> | Zu Loot-Kiste teleportieren | survivalplus.lootchests |
| `/stats` | Spielerstatistiken | `survivalplus.stats` | | /trade <Spieler> | Handel starten | survivalplus.trade |
| `/report <spieler> [grund]` | Spieler melden | `survivalplus.report` | | /tradeaccept <Spieler> | Handel annehmen | survivalplus.tradeaccept |
| `/showreport <spieler>` | Reports anzeigen | `survivalplus.report.show` | | /day | Tag setzen | survivalplus.day |
| `/clearreport <spieler>` | Reports löschen | `survivalplus.report.clear` | | /night | Nacht setzen | survivalplus.night |
| /gm <Modus> | Gamemode ändern | survivalplus.gamemode |
--- | /heal [Spieler] | Spieler heilen | survivalplus.heal |
| /friend ... | Freundesverwaltung | survivalplus.friend |
## ⚙ Installation | /block <Spieler> | Spieler blockieren | survivalplus.block |
| /unblock <Spieler> | Blockierung aufheben | survivalplus.unblock |
1. Lade die aktuelle `.jar` Datei herunter. | /blocklist | Blockliste anzeigen | survivalplus.blocklist |
2. Kopiere sie in den `plugins`-Ordner deines Servers. | /report <Spieler> [Grund] | Spieler melden | survivalplus.report |
3. Starte den Server neu oder nutze `/reload`. | /showreport <Spieler> | Reports anzeigen | survivalplus.report.show |
4. Stelle sicher, dass **LuckPerms** und **PlaceholderAPI** installiert sind. | /clearreport <Spieler> | Reports löschen | survivalplus.report.clear |
| /stats | Spielerstatistiken | survivalplus.stats |
| /kit | Starterkit | survivalplus.kit |
| /startchallenge <Name> | Fun-Challenge starten | survivalplus.startchallenge |
| /sit | Hinsetzen | survivalplus.sit |
--- ---
## 🔐 Permissions ## 🔐 Permissions
- **Vollzugriff:** `survivalplus.*` (OP) - `survivalplus.*` — Vollzugriff (OP)
- Alle Befehle können über LuckPerms angepasst werden. - `survivalplus.sp` — Zugriff auf Hauptbefehl
- `survivalplus.claim.use / trust / admin` — Claim-System
- `survivalplus.vanish.silent` — Silent Join
- `survivalplus.vanish.no-pickup` — Keine Item-Aufnahme
- `survivalplus.head``/head` nutzen
- `survivalplus.notify` — Admin-Benachrichtigungen
➡ Vollständige Liste: **plugin.yml**
--- ---
## 📜 Lizenz ## ⚙️ Installation & Setup
Dieses Projekt ist frei für den privaten Gebrauch. Kontakt: **M_Viper** 1. Aktuelle `.jar` herunterladen
2. In den `plugins/` Ordner legen
3. **LuckPerms** und **Vault** installieren (empfohlen)
4. Server neu starten oder `/reload`
5. Konfigurationsdateien (`config.yml`, `lang.yml`, `help.yml`, etc.) nach Wunsch anpassen
---
## ⚙️ Konfiguration & Platzhalter
- **Alle Nachrichten**: In `lang.yml` anpassbar (außer feste Plugin-Infos)
- **Hilfe & Gruppen**: In `help.yml` strukturierbar
- **Tablist**: In `tablist.yml` animierbar
- **Shop, Claims, Homes, etc.**: Eigene YAML-Dateien
- **Platzhalter**: `%player%`, `%x%`, `%y%`, `%z%`, `%world%`, `{item}`, `{price}`, `{stock}` u.v.m.
---
## 🐞 Debug & Fehlerberichte
- In `config.yml` aktivieren:
```yml
debug-logging: true
```
- Logs: `Debug/debug.log` (Fehler, Stacktraces), `Debug/console.log` (Konsolenausgabe)
---
## 📊 Statistiken & bStats
- Anonyme Nutzungsstatistiken via [bStats](https://bstats.org/plugin/bukkit/SurvivalPlus)
---
## 📜 Lizenz & Support
Dieses Projekt ist **frei für privaten Gebrauch**.
**Kontakt / Support / Feature-Wünsche:**
- [SpigotMC-Profil](https://www.spigotmc.org/resources/authors/m-lukas-17.618600/)
- [Bugtracker](https://git.viper.ipv64.net/M_Viper/Survival-Plus/issues)
---
## ❓ FAQ & Hinweise
- **Welche Minecraft-Version?** 1.21+ (Paper/Spigot/Bukkit)
- **Welche Plugins werden unterstützt?** LuckPerms, Vault, PlaceholderAPI, ProtocolLib (softdepend)
- **Wie kann ich alle Texte anpassen?** Über `lang.yml` und `help.yml`
- **Wie kann ich Claims, Shops, Homes sichern?** YAML-Dateien im `plugins/SurvivalPlus/`-Ordner
- **Wie kann ich Fehler melden?** Siehe Support-Links oben
---
> **Hinweis:**
> Alle Commands sind vollständig in der `plugin.yml` definiert.
> Nachfolgend eine Übersicht nach Kategorien.
---
### Allgemein / Haupt
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/sp` | Hauptbefehl (Reload, Info, Help, Share, CommandBlocker u.v.m.) | `survivalplus.sp` |
| `/help` | Hilfe / Übersicht (Alias: `/sp help`) | `survivalplus.sp` |
---
### Item & Utility
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/ir <name>` | Item umbenennen (Item in Hand) | `survivalplus.itemrename` |
| `/workbench` | Öffnet Werkbank-GUI | `survivalplus.workbench` |
| `/anvil` | Öffnet Amboss-GUI | `survivalplus.anvil` |
| `/trash` | Öffnet Mülleimer | `survivalplus.trash` |
| `/showarmorstands` | Debug: ArmorStands sichtbar | `survivalplus.showarmorstands` |
| `/cleardebugarmorstands` | Entfernt Debug-ArmorStands | `survivalplus.cleardebugarmorstands` |
| `/leashcount` | Zeigt geleinte Tiere | `survivalplus.leashcount` |
| `/nick <Name>` | Nickname setzen (Farben/Hex) | `survivalplus.nick` |
| `/nick off` | Nickname entfernen | `survivalplus.nick` |
| `/head <Spieler>` | Spieler-Kopf erhalten | `survivalplus.head` |
---
### Admin & Tools
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/vanish` | Unsichtbar (Silent, No-Pickup optional) | `survivalplus.vanish` |
| `/freeze <Spieler>` | Spieler einfrieren | `survivalplus.freeze` |
| `/ride [Spieler]` | Reite einen Spieler | `survivalplus.ride` |
---
### Teleportation
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/tp <Spieler>` | Teleport zu Spieler | `survivalplus.tp` |
| `/tphere <Spieler>` | Spieler zu dir teleportieren | `survivalplus.tphere` |
| `/tpa <Spieler>` | Teleportanfrage senden | `survivalplus.tpa` |
| `/tpaccept` | Anfrage akzeptieren | `survivalplus.tpaccept` |
| `/tpdeny` | Anfrage ablehnen | `survivalplus.tpdeny` |
| `/back` | Zum letzten Todespunkt | `survivalplus.back` |
| `/spawn` | Zum Weltspawn | `survivalplus.spawn` |
| `/setspawn` | Server-Spawn setzen | `survivalplus.setspawn` |
| `/setworldspawn` | Welt-Spawn setzen | `survivalplus.setworldspawn` |
---
### Homes & Warps
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/sethome <name>` | Home setzen | `survivalplus.homes.set` |
| `/delhome <name>` | Home löschen | `survivalplus.homes.delete` |
| `/home <name>` | Zu Home teleportieren | `survivalplus.homes` |
| `/homelist` | GUI mit allen Homes | `survivalplus.homes.list` |
| `/setwarp <name>` | Persönlichen Warp setzen | `survivalplus.setwarp` |
| `/delwarp <name>` | Warp löschen | `survivalplus.delwarp` |
| `/warps` | Warp-GUI öffnen | `survivalplus.warps` |
---
### Claim (Anti-Grief)
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/claim mark 1|2` | Auswahlpunkt setzen | — |
| `/claim` | Gebiet claimen | `survivalplus.claim.use` |
| `/claim unclaim` | Claim löschen | `survivalplus.claim.use` |
| `/claim unclaim <Spieler>` | Alle Claims löschen (Admin) | `survivalplus.claim.admin` |
| `/claim trust <Spieler>` | Spieler hinzufügen | `survivalplus.claim.trust` |
| `/claim untrust <Spieler>` | Spieler entfernen | `survivalplus.claim.trust` |
| `/claim ban <Spieler>` | Spieler bannen | `survivalplus.claim.ban` |
| `/claim kick <Spieler>` | Spieler kicken | `survivalplus.claim.kick` |
| `/claim info` | Claim-Infos anzeigen | `survivalplus.claim.use` |
---
### CommandBlocker / Server-Management
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/sp cb add <cmd>` | Befehl blockieren | `survivalplus.commandblocker.add` |
| `/sp cb remove <cmd>` | Blockierung entfernen | `survivalplus.commandblocker.remove` |
| `/sp cb list` | Blockierte Befehle | `survivalplus.commandblocker.list` |
| `/clearchat` | Chat leeren | `survivalplus.clearchat` |
| `/clearitems` | Items entfernen | `survivalplus.clearitems` |
| `/closedoors <radius>` | Türen schließen | `survivalplus.closedoors` |
| `/sp lock` | Kisten/Türen sperren | `survivalplus.lock` |
---
### Shop, Loot & Trade
| Befehl | Nutzung | Permission |
|------|------|-----------|
| **Sign Shops** | `[Buy] Menge Item Preis` | automatisch |
| `/shop add <item> <preis> <bestand>` | Shop verwalten | `survivalplus.shop` |
| `/lootchests` | Loot-Kisten auflisten | `survivalplus.lootchests` |
| `/tploot <welt> <x> <y> <z>` | Zu Loot-Kiste teleportieren | `survivalplus.lootchests` |
| `/trade <Spieler>` | Handel starten | `survivalplus.trade` |
| `/tradeaccept <Spieler>` | Handel annehmen | `survivalplus.tradeaccept` |
---
### Zeit, Gamemode & Admin
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/day` | Tag setzen | `survivalplus.day` |
| `/night` | Nacht setzen | `survivalplus.night` |
| `/gm <modus>` | Gamemode ändern | `survivalplus.gamemode` |
| `/heal [Spieler]` | Spieler heilen | `survivalplus.heal` |
---
### Freundes-, Block- & Report-System
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/friend add|accept|deny` | Freundesverwaltung | — |
| `/block <Spieler>` | Spieler blockieren | `survivalplus.block` |
| `/unblock <Spieler>` | Blockierung aufheben | `survivalplus.unblock` |
| `/blocklist` | Blockliste anzeigen | `survivalplus.blocklist` |
| `/report <Spieler> [Grund]` | Spieler melden | `survivalplus.report` |
| `/showreport <Spieler>` | Reports anzeigen | `survivalplus.report.show` |
| `/clearreport <Spieler>` | Reports löschen | `survivalplus.report.clear` |
---
### Sonstiges
| Befehl | Nutzung | Permission |
|------|------|-----------|
| `/stats` | Spielerstatistiken | `survivalplus.stats` |
| `/kit` | Starterkit | `survivalplus.kit` |
| `/startchallenge <name>` | Fun-Challenge starten | `survivalplus.startchallenge` |
| `/sit` | Hinsetzen | `survivalplus.sit` |
---
## 🔐 Permissions (Kurzüberblick)
- `survivalplus.*` — Vollzugriff (OP)
- `survivalplus.sp` — Zugriff auf Hauptbefehl
- `survivalplus.claim.use / trust / admin` — Claim-System
- `survivalplus.vanish.silent` — Silent Join
- `survivalplus.vanish.no-pickup` — Keine Item-Aufnahme
- `survivalplus.head` — `/head` nutzen
- `survivalplus.notify` — Admin-Benachrichtigungen
➡ Vollständige Liste: **plugin.yml**
---
## ⚙ Installation
1. Aktuelle `.jar` herunterladen
2. In den `plugins/` Ordner legen
3. **LuckPerms** und **Vault** installieren
4. Server neu starten oder `/reload`
---
## 🐞 Debug & Fehlerberichte
- In `config.yml` setzen:
```yml
debug-logging: true
```
## 🐞 Debug & Logs
- `Debug/debug.log` — Enthält Plugin-Fehler und Stacktraces (nur bei aktiviertem Debug-Logging)
- `Debug/console.log` — Kompletter Konsolenoutput zur Weitergabe an Entwickler
---
## 📜 Lizenz & Kontakt
Dieses Projekt ist frei für den **privaten Gebrauch**.
**Kontakt / Support / Feature-Wünsche:**
**M_Viper** (Repo-Owner / GitHub)

View File

@@ -1,30 +1,83 @@
package de.viper.survivalplus.Manager; package de.viper.survivalplus.Manager;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.util.*; import java.util.*;
import org.bukkit.entity.Player; import de.viper.survivalplus.SurvivalPlus;
public class BlockManager { public class BlockManager {
private final SurvivalPlus plugin;
private final Map<UUID, Set<UUID>> blockedPlayers = new HashMap<>(); private final Map<UUID, Set<UUID>> blockedPlayers = new HashMap<>();
private File blocksFile;
private FileConfiguration blocksConfig;
public BlockManager(SurvivalPlus plugin) {
this.plugin = plugin;
this.blocksFile = new File(plugin.getDataFolder(), "blocks.yml");
if (!blocksFile.exists()) {
try {
blocksFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
this.blocksConfig = YamlConfiguration.loadConfiguration(blocksFile);
loadBlocks();
}
private void saveBlocks() {
for (UUID key : blockedPlayers.keySet()) {
List<String> uuids = new ArrayList<>();
for (UUID uuid : blockedPlayers.get(key)) {
uuids.add(uuid.toString());
}
blocksConfig.set(key.toString(), uuids);
}
try {
blocksConfig.save(blocksFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadBlocks() {
blockedPlayers.clear();
for (String key : blocksConfig.getKeys(false)) {
UUID blockerUUID = UUID.fromString(key);
List<String> blockedUUIDs = blocksConfig.getStringList(key);
Set<UUID> blockedSet = new HashSet<>();
for (String uuidStr : blockedUUIDs) {
blockedSet.add(UUID.fromString(uuidStr));
}
blockedPlayers.put(blockerUUID, blockedSet);
}
}
public void blockPlayer(Player blocker, Player toBlock) { public void blockPlayer(Player blocker, Player toBlock) {
blockedPlayers.computeIfAbsent(blocker.getUniqueId(), k -> new HashSet<>()).add(toBlock.getUniqueId()); blockedPlayers.computeIfAbsent(blocker.getUniqueId(), k -> new HashSet<>()).add(toBlock.getUniqueId());
saveBlocks(); // Sofort speichern
} }
public void unblockPlayer(Player blocker, Player toUnblock) { public void unblockPlayer(Player blocker, Player toUnblock) {
Set<UUID> blocked = blockedPlayers.get(blocker.getUniqueId()); Set<UUID> blocked = blockedPlayers.get(blocker.getUniqueId());
if (blocked != null) { if (blocked != null) {
blocked.remove(toUnblock.getUniqueId()); blocked.remove(toUnblock.getUniqueId());
if (blocked.isEmpty()) { if (blocked.isEmpty()) {
blockedPlayers.remove(blocker.getUniqueId()); blockedPlayers.remove(blocker.getUniqueId());
}
saveBlocks(); // Sofort speichern
} }
} }
}
public boolean hasBlocked(Player blocker, Player potentialBlocked) { public boolean hasBlocked(Player blocker, Player potentialBlocked) {
return blockedPlayers.getOrDefault(blocker.getUniqueId(), Collections.emptySet()) return blockedPlayers.getOrDefault(blocker.getUniqueId(), Collections.emptySet())
.contains(potentialBlocked.getUniqueId()); .contains(potentialBlocked.getUniqueId());
} }
public Set<UUID> getBlockedPlayers(Player player) { public Set<UUID> getBlockedPlayers(Player player) {
@@ -33,5 +86,6 @@ public class BlockManager {
public void clear(Player player) { public void clear(Player player) {
blockedPlayers.remove(player.getUniqueId()); blockedPlayers.remove(player.getUniqueId());
saveBlocks();
} }
} }

View File

@@ -6,18 +6,21 @@ import org.bukkit.*;
import org.bukkit.block.Chest; import org.bukkit.block.Chest;
import org.bukkit.command.*; import org.bukkit.command.*;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
@@ -34,9 +37,17 @@ public class LootChestManager implements Listener, CommandExecutor {
private final int maxLootPerPlayer; private final int maxLootPerPlayer;
private final long lootLimitResetMillis; private final long lootLimitResetMillis;
// Persistence
private final File lootChestFile;
private FileConfiguration lootChestConfig;
public LootChestManager(SurvivalPlus plugin) { public LootChestManager(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.lootChestFile = new File(plugin.getDataFolder(), "lootchests.yml");
this.lootChestConfig = YamlConfiguration.loadConfiguration(lootChestFile);
loadLootFromConfig(); loadLootFromConfig();
loadActiveChests(); // Kisten beim Start laden
FileConfiguration cfg = plugin.getConfig(); FileConfiguration cfg = plugin.getConfig();
this.despawnMillis = cfg.getLong("lootchest.despawn-minutes", 10) * 60 * 1000; this.despawnMillis = cfg.getLong("lootchest.despawn-minutes", 10) * 60 * 1000;
@@ -49,6 +60,102 @@ public class LootChestManager implements Listener, CommandExecutor {
startLootLimitResetTask(); startLootLimitResetTask();
} }
/**
* Lädt gespeicherte Kisten aus der Datei und prüft, ob sie abgelaufen sind
*/
private void loadActiveChests() {
if (!lootChestFile.exists()) return;
long now = System.currentTimeMillis();
List<Location> toRemoveFile = new ArrayList<>();
for (String key : lootChestConfig.getKeys(false)) {
try {
long spawnTime = lootChestConfig.getLong(key);
// Prüfen ob die Zeit abgelaufen ist (inklusive Offline-Zeit)
if (now - spawnTime >= despawnMillis) {
String[] parts = key.split("_");
if (parts.length == 4) {
World w = Bukkit.getWorld(parts[0]);
if (w != null) {
int x = Integer.parseInt(parts[1]);
int y = Integer.parseInt(parts[2]);
int z = Integer.parseInt(parts[3]);
Location loc = new Location(w, x, y, z);
// Kiste physikalisch löschen falls noch vorhanden
if (loc.getBlock().getType() == Material.CHEST) {
loc.getBlock().setType(Material.AIR);
}
toRemoveFile.add(loc);
}
}
} else {
// Kiste ist noch gültig -> in den Speicher laden
String[] parts = key.split("_");
if (parts.length == 4) {
World w = Bukkit.getWorld(parts[0]);
if (w != null) {
int x = Integer.parseInt(parts[1]);
int y = Integer.parseInt(parts[2]);
int z = Integer.parseInt(parts[3]);
Location loc = new Location(w, x, y, z);
// Integritätsprüfung: Ist da auch wirklich eine Kiste?
if (loc.getBlock().getType() == Material.CHEST) {
activeChests.add(loc);
chestSpawnTimes.put(loc, spawnTime);
} else {
// Datei sagt ja, Welt sagt nein -> Cleanup in Datei
toRemoveFile.add(loc);
}
}
}
}
} catch (Exception e) {
plugin.getLogger().warning("Fehler beim Laden einer Lootkiste: " + key);
}
}
// Alte Einträge aus der Datei entfernen
for (Location loc : toRemoveFile) {
String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
lootChestConfig.set(key, null);
}
saveActiveChests();
}
/**
* Speichert die aktuellen aktiven Kisten in die Datei
*/
private void saveActiveChests() {
lootChestConfig = new YamlConfiguration();
for (Map.Entry<Location, Long> entry : chestSpawnTimes.entrySet()) {
Location loc = entry.getKey();
String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
lootChestConfig.set(key, entry.getValue());
}
try {
lootChestConfig.save(lootChestFile);
} catch (IOException e) {
plugin.getLogger().severe("Konnte lootchests.yml nicht speichern!");
e.printStackTrace();
}
}
/**
* Entfernt eine Kiste komplett aus System und Datei (z.B. beim Looten)
*/
private void removeChestData(Location loc) {
activeChests.remove(loc);
chestSpawnTimes.remove(loc);
String key = loc.getWorld().getName() + "_" + loc.getBlockX() + "_" + loc.getBlockY() + "_" + loc.getBlockZ();
lootChestConfig.set(key, null);
saveActiveChests();
}
private void loadLootFromConfig() { private void loadLootFromConfig() {
FileConfiguration cfg = plugin.getConfig(); FileConfiguration cfg = plugin.getConfig();
lootItems.clear(); lootItems.clear();
@@ -75,7 +182,11 @@ public class LootChestManager implements Listener, CommandExecutor {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
// Falls deaktiviert, nichts tun
// Hinweis: Die Logik hier war "!enabled ... return; clearAll".
// Das bedeutet: Wenn enabled=true -> !enabled=false -> kein return -> clearAll.
if (!plugin.getConfig().getBoolean("lootchest.enabled", true)) return; if (!plugin.getConfig().getBoolean("lootchest.enabled", true)) return;
clearAllActiveChests(); clearAllActiveChests();
for (int i = 0; i < spawnCount; i++) { for (int i = 0; i < spawnCount; i++) {
spawnRandomLootChest(); spawnRandomLootChest();
@@ -88,26 +199,36 @@ public class LootChestManager implements Listener, CommandExecutor {
} }
private void startChestDespawnTask() { private void startChestDespawnTask() {
// Prüft jede Minute (20 Ticks * 60)
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Sicheres Iterieren um ConcurrentModificationException zu vermeiden
Iterator<Location> it = activeChests.iterator(); Iterator<Location> it = activeChests.iterator();
while (it.hasNext()) { while (it.hasNext()) {
Location loc = it.next(); Location loc = it.next();
Long spawnTime = chestSpawnTimes.get(loc); Long spawnTime = chestSpawnTimes.get(loc);
if (spawnTime != null && now - spawnTime >= despawnMillis) { if (spawnTime != null && now - spawnTime >= despawnMillis) {
if (loc.getBlock().getType() == Material.CHEST) { if (loc.getBlock().getType() == Material.CHEST) {
loc.getBlock().setType(Material.AIR); loc.getBlock().setType(Material.AIR);
} }
it.remove();
// Manuell aus den Datenstrukturen entfernen
chestSpawnTimes.remove(loc); chestSpawnTimes.remove(loc);
it.remove(); // Aus der Liste entfernen (Thread-Safe)
Bukkit.broadcastMessage(color( Bukkit.broadcastMessage(color(
plugin.getLangConfig().getString("lootchest.despawn-msg", plugin.getLangConfig().getString("lootchest.despawn-msg",
"&eEine Lootkiste ist von selbst verschwunden.") "&eEine Lootkiste ist von selbst verschwunden.")
)); ));
} }
} }
// Config einmal speichern (wichtig!)
saveActiveChests();
} }
}.runTaskTimer(plugin, 20L * 60, 20L * 60); }.runTaskTimer(plugin, 20L * 60, 20L * 60);
} }
@@ -117,6 +238,7 @@ public class LootChestManager implements Listener, CommandExecutor {
if (loc.getBlock().getType() == Material.CHEST) { if (loc.getBlock().getType() == Material.CHEST) {
loc.getBlock().setType(Material.AIR); loc.getBlock().setType(Material.AIR);
} }
removeChestData(loc);
} }
activeChests.clear(); activeChests.clear();
chestSpawnTimes.clear(); chestSpawnTimes.clear();
@@ -158,6 +280,8 @@ public class LootChestManager implements Listener, CommandExecutor {
activeChests.add(loc); activeChests.add(loc);
chestSpawnTimes.put(loc, System.currentTimeMillis()); chestSpawnTimes.put(loc, System.currentTimeMillis());
saveActiveChests();
world.spawnParticle(Particle.FIREWORK, loc.clone().add(0.5, 1.0, 0.5), world.spawnParticle(Particle.FIREWORK, loc.clone().add(0.5, 1.0, 0.5),
20, 0.3, 0.5, 0.3); 20, 0.3, 0.5, 0.3);
world.playSound(loc, Sound.BLOCK_CHEST_OPEN, 1.0f, 1.0f); world.playSound(loc, Sound.BLOCK_CHEST_OPEN, 1.0f, 1.0f);
@@ -265,8 +389,9 @@ public class LootChestManager implements Listener, CommandExecutor {
if (looted >= maxLootPerPlayer) return; if (looted >= maxLootPerPlayer) return;
playerLootCount.put(uuid, looted + 1); playerLootCount.put(uuid, looted + 1);
chestLoc.getBlock().setType(Material.AIR); chestLoc.getBlock().setType(Material.AIR);
activeChests.remove(chestLoc);
chestSpawnTimes.remove(chestLoc); removeChestData(chestLoc);
Bukkit.broadcastMessage(color( Bukkit.broadcastMessage(color(
plugin.getLangConfig().getString("lootchest.removed-msg", "&aEine Lootkiste wurde geleert und entfernt.") plugin.getLangConfig().getString("lootchest.removed-msg", "&aEine Lootkiste wurde geleert und entfernt.")
)); ));

View File

@@ -13,6 +13,10 @@ public class ShopManager {
private final File shopFile; private final File shopFile;
private final FileConfiguration shopConfig; private final FileConfiguration shopConfig;
// Multiplikatoren für dynamische Preise
private double buyMultiplier;
private double sellMultiplier;
public ShopManager(SurvivalPlus plugin) { public ShopManager(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.shopFile = new File(plugin.getDataFolder(), "shop.yml"); this.shopFile = new File(plugin.getDataFolder(), "shop.yml");
@@ -20,38 +24,77 @@ public class ShopManager {
if (!shopFile.exists()) { if (!shopFile.exists()) {
plugin.saveResource("shop.yml", false); plugin.saveResource("shop.yml", false);
} }
this.shopConfig = YamlConfiguration.loadConfiguration(shopFile); this.shopConfig = YamlConfiguration.loadConfiguration(shopFile);
// Multiplikatoren laden
loadMultipliers();
}
public void loadMultipliers() {
this.buyMultiplier = plugin.getConfig().getDouble("economy.buy-multiplier", 1.05);
this.sellMultiplier = plugin.getConfig().getDouble("economy.sell-multiplier", 0.95);
plugin.getLogger().info("Shop-Multiplikatoren geladen: Buy=" + buyMultiplier + ", Sell=" + sellMultiplier);
} }
public double getCurrentPrice(String itemKey) { public double getCurrentPrice(String itemKey) {
return shopConfig.getDouble("items." + itemKey + ".current-price", 0); return shopConfig.getDouble("items." + itemKey + ".current-price", 0);
} }
public int getStock(String itemKey) {
return shopConfig.getInt("items." + itemKey + ".stock", 0);
}
/**
* Spieler kauft Item: Preis steigt
*/
public boolean buyItem(String itemKey, int amount) { public boolean buyItem(String itemKey, int amount) {
int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); int stock = shopConfig.getInt("items." + itemKey + ".stock", 0);
if (stock < amount) {
return false; // Fallback auf Base-Price wenn noch keine Transaktion stattfand (sonst wird 1.05 auf 0 gerechnet)
double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0);
if (currentPrice == 0) {
currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100);
} }
double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0);
shopConfig.set("items." + itemKey + ".stock", stock - amount); // Lager aktualisieren (optional, oder unendlich)
shopConfig.set("items." + itemKey + ".current-price", price * 0.95); // shopConfig.set("items." + itemKey + ".stock", stock - amount);
// Preis erhöhen (Dynamisch)
double newPrice = currentPrice * buyMultiplier;
shopConfig.set("items." + itemKey + ".current-price", newPrice);
saveShop(); saveShop();
return true; return true;
} }
public void sellItem(String itemKey, int amount) { /**
int stock = shopConfig.getInt("items." + itemKey + ".stock", 0); * Spieler verkauft Item: Preis sinkt
double price = shopConfig.getDouble("items." + itemKey + ".current-price", 0); */
shopConfig.set("items." + itemKey + ".stock", stock + amount); public boolean sellItem(String itemKey, int amount) {
shopConfig.set("items." + itemKey + ".current-price", price * 0.95); // Fallback auf Base-Price
double currentPrice = shopConfig.getDouble("items." + itemKey + ".current-price", 0);
if (currentPrice == 0) {
currentPrice = shopConfig.getDouble("items." + itemKey + ".base-price", 100);
}
// Lager aktualisieren (optional)
// shopConfig.set("items." + itemKey + ".stock", stock + amount);
// Preis senken (Dynamisch)
double newPrice = currentPrice * sellMultiplier;
shopConfig.set("items." + itemKey + ".current-price", newPrice);
saveShop(); saveShop();
return true;
} }
public void addOrUpdateItem(String itemKey, double basePrice, int stock) { public void addOrUpdateItem(String itemKey, double basePrice, int stock) {
shopConfig.set("items." + itemKey + ".base-price", basePrice); shopConfig.set("items." + itemKey + ".base-price", basePrice);
shopConfig.set("items." + itemKey + ".stock", stock); shopConfig.set("items." + itemKey + ".stock", stock);
shopConfig.set("items." + itemKey + ".current-price", basePrice); // Wenn der Preis noch nicht existiert (neues Item), setze ihn auf Base-Preis
if (!shopConfig.contains("items." + itemKey + ".current-price")) {
shopConfig.set("items." + itemKey + ".current-price", basePrice);
}
saveShop(); saveShop();
} }

View File

@@ -1,30 +1,30 @@
package de.viper.survivalplus.Manager; package de.viper.survivalplus.Manager;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import me.clip.placeholderapi.PlaceholderAPI;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.user.User;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager; import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.scoreboard.Team; import org.bukkit.scoreboard.Team;
import me.clip.placeholderapi.PlaceholderAPI;
import net.luckperms.api.LuckPerms;
import net.luckperms.api.LuckPermsProvider;
import net.luckperms.api.model.user.User;
import java.io.File; import java.io.File;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.*;
import java.util.Date; import java.util.regex.Matcher;
import java.util.HashMap; import java.util.regex.Pattern;
import java.util.List;
import java.util.Map;
public class TablistManager implements Listener { public class TablistManager implements Listener {
@@ -44,39 +44,33 @@ public class TablistManager implements Listener {
private String separatorLine; private String separatorLine;
private LuckPerms luckPerms; private LuckPerms luckPerms;
private boolean hasPlaceholderAPI; private boolean hasPlaceholderAPI;
private final Scoreboard scoreboard;
private final Map<String, Team> prefixTeams;
private FileConfiguration nicknameConfig; private FileConfiguration nicknameConfig;
private final Scoreboard scoreboard;
private final Map<String, Team> prefixTeams = new HashMap<>();
private static final Pattern HEX_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
public TablistManager(SurvivalPlus plugin) { public TablistManager(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.prefixTeams = new HashMap<>();
// Scoreboard initialisieren // Scoreboard initialisieren
ScoreboardManager scoreboardManager = Bukkit.getScoreboardManager(); ScoreboardManager scoreboardManager = Bukkit.getScoreboardManager();
this.scoreboard = scoreboardManager != null ? scoreboardManager.getMainScoreboard() : null; this.scoreboard = scoreboardManager != null ? scoreboardManager.getMainScoreboard() : null;
// Resource sicherstellen, Config laden
try { try {
plugin.saveResource("tablist.yml", false); File tablistFile = new File(plugin.getDataFolder(), "tablist.yml");
if (!tablistFile.exists()) {
plugin.saveResource("tablist.yml", false);
}
} catch (Exception ignored) {} } catch (Exception ignored) {}
try { try {
plugin.reloadTablistConfig(); plugin.reloadTablistConfig();
} catch (Exception ignored) {} } catch (Exception ignored) {}
FileConfiguration config = plugin.getTablistConfig(); FileConfiguration config = plugin.getTablistConfig();
// Nicknames.yml laden
try {
File nicknameFile = new File(plugin.getDataFolder(), "nicknames.yml");
if (!nicknameFile.exists()) {
plugin.saveResource("nicknames.yml", false);
}
this.nicknameConfig = YamlConfiguration.loadConfiguration(nicknameFile);
} catch (Exception ignored) {
this.nicknameConfig = null; // Keine Konsolenausgabe
}
// Konfigurationswerte laden
this.enabled = config.getBoolean("enabled", true); this.enabled = config.getBoolean("enabled", true);
this.serverName = config.getString("server-name", "SurvivalPlus"); this.serverName = config.getString("server-name", "SurvivalPlus");
this.website = config.getString("website", "www.example.com"); this.website = config.getString("website", "www.example.com");
@@ -87,27 +81,22 @@ public class TablistManager implements Listener {
this.staffPermission = config.getString("staff-permission", "survivalplus.staff"); this.staffPermission = config.getString("staff-permission", "survivalplus.staff");
this.separatorLine = config.getString("separator-line", "&8&l&m================"); this.separatorLine = config.getString("separator-line", "&8&l&m================");
// LuckPerms API initialisieren
try { try {
this.luckPerms = LuckPermsProvider.get(); this.luckPerms = LuckPermsProvider.get();
} catch (Throwable e) { } catch (Throwable e) {
luckPerms = null; // Keine Konsolenausgabe luckPerms = null;
} }
// Prüfen, ob PlaceholderAPI verfügbar ist
this.hasPlaceholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); this.hasPlaceholderAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI");
// Chat-Listener registrieren // Registriere Chat-Listener hier, damit Prefix im Chat gesetzt wird
if (enabled) { Bukkit.getPluginManager().registerEvents(this, plugin);
Bukkit.getPluginManager().registerEvents(this, plugin);
}
if (!enabled) { if (!enabled) {
plugin.getLogger().info("Tablist ist deaktiviert (tablist.yml -> enabled: false)"); plugin.getLogger().info("Tablist ist deaktiviert (tablist.yml -> enabled: false)");
return; return;
} }
// Header- und Footer-Animationen füllen
List<String> configHeader = config.getStringList("header-animations"); List<String> configHeader = config.getStringList("header-animations");
List<String> configFooter = config.getStringList("footer-animations"); List<String> configFooter = config.getStringList("footer-animations");
@@ -134,40 +123,49 @@ public class TablistManager implements Listener {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
reloadNicknameConfig();
if (headerAnim.isEmpty() || footerAnim.isEmpty()) return; if (headerAnim.isEmpty() || footerAnim.isEmpty()) return;
// Online-Spieler und Staff zählen
int onlinePlayers = Bukkit.getOnlinePlayers().size(); int onlinePlayers = Bukkit.getOnlinePlayers().size();
int onlineStaff = (int) Bukkit.getOnlinePlayers().stream() int onlineStaff = (int) Bukkit.getOnlinePlayers().stream()
.filter(p -> p.hasPermission(staffPermission)) .filter(p -> p.hasPermission(staffPermission))
.count(); .count();
// Aktuelles Datum und Uhrzeit formatieren
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
String currentTime = dateFormat.format(new Date()); String currentTime = dateFormat.format(new Date());
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
try { try {
// Nickname oder Spielername verwenden // ------ Name / Nick handling exactly like in your old code ------
String displayName = getNickname(player); String displayName = getNickname(player);
if (displayName == null || displayName.trim().isEmpty()) { if (displayName == null || displayName.trim().isEmpty()) {
displayName = player.getName(); displayName = player.getName();
} }
// Spielername für die Tablist setzen String rankPrefix = getPlayerPrefix(player);
// Update nametag (prefix above head) as in old code:
updateNametag(player, rankPrefix, getNickname(player)); // pass raw nickname (colored) if present
// PlayerList (TAB)
int ping = getPlayerPing(player);
String playerListName; String playerListName;
String prefix = getPlayerPrefix(player);
if (luckPerms == null && !hasPlaceholderAPI) { if (luckPerms == null && !hasPlaceholderAPI) {
playerListName = displayName; playerListName = displayName;
updateNametag(player, "", displayName);
} else { } else {
int ping = getPlayerPing(player); playerListName = color(rankPrefix + displayName + (ping >= 0 ? "&8 | &e" + ping + "ms" : ""));
playerListName = color(prefix + displayName + (ping >= 0 ? "&8 | &e" + ping + "ms" : ""));
updateNametag(player, prefix, displayName);
} }
player.setPlayerListName(playerListName);
// Header mit Spielername/Nickname und Statistiken try {
player.setPlayerListName(playerListName);
} catch (Exception ignored) {
// safe fallback: set without prefix
try {
player.setPlayerListName(color(displayName));
} catch (Exception ignored2) {}
}
// Header/Footer build (including TS/Discord/Website and time)
String headerRaw = headerAnim.get(headerIndex) String headerRaw = headerAnim.get(headerIndex)
.replace("{server}", serverName) .replace("{server}", serverName)
.replace("{player}", displayName) .replace("{player}", displayName)
@@ -175,11 +173,9 @@ public class TablistManager implements Listener {
.replace("{staff}", String.valueOf(onlineStaff)); .replace("{staff}", String.valueOf(onlineStaff));
String footerRaw = footerAnim.get(footerIndex); String footerRaw = footerAnim.get(footerIndex);
// Footer zusammenstellen: TS und Discord nebeneinander, Webseite zentriert
StringBuilder footerBuilder = new StringBuilder(); StringBuilder footerBuilder = new StringBuilder();
footerBuilder.append("\n"); // Extra Abstand footerBuilder.append("\n");
footerBuilder.append(color(footerRaw)).append("\n"); footerBuilder.append(color(footerRaw)).append("\n");
footerBuilder.append(color(separatorLine)).append("\n"); footerBuilder.append(color(separatorLine)).append("\n");
if (showTeamspeak || showDiscord) { if (showTeamspeak || showDiscord) {
StringBuilder socialLine = new StringBuilder(); StringBuilder socialLine = new StringBuilder();
@@ -201,28 +197,40 @@ public class TablistManager implements Listener {
String header = color(headerRaw); String header = color(headerRaw);
String footer = color(footerBuilder.toString()); String footer = color(footerBuilder.toString());
// 1) Adventure (Component) Variante // Try Adventure-based header/footer first, fallback to String reflection
boolean done = tryAdventureComponent(player, headerRaw, footerBuilder.toString()); boolean done = tryAdventureComponent(player, headerRaw, footerBuilder.toString());
// 2) String-Variante fallback
if (!done) { if (!done) {
done = tryStringMethod(player, header, footer); tryStringMethod(player, header, footer);
} }
// 3) Keine Warnung bei Fehlschlag, da dies normal sein kann } catch (Exception ex) {
} catch (Exception ignored) { plugin.getLogger().warning("TablistManager error: " + ex.getMessage());
// Keine Konsolenausgabe für Fehler bei der Tablist
} }
} }
headerIndex = (headerIndex + 1) % headerAnim.size(); headerIndex = (headerIndex + 1) % headerAnim.size();
footerIndex = (footerIndex + 1) % footerAnim.size(); footerIndex = (footerIndex + 1) % footerAnim.size();
} }
}.runTaskTimer(plugin, 0L, interval); }.runTaskTimer(plugin, 40L, interval);
} }
/** /**
* Nickname aus nicknames.yml abrufen * Lädt nicknames.yml neu.
*/
private void reloadNicknameConfig() {
try {
File nicknameFile = new File(plugin.getDataFolder(), "nicknames.yml");
if (!nicknameFile.exists()) {
plugin.saveResource("nicknames.yml", false);
}
this.nicknameConfig = YamlConfiguration.loadConfiguration(nicknameFile);
} catch (Exception ignored) {
this.nicknameConfig = null;
}
}
/**
* Nickname abrufen und formatieren (farbig + hex)
*/ */
private String getNickname(Player player) { private String getNickname(Player player) {
if (nicknameConfig == null) return null; if (nicknameConfig == null) return null;
@@ -230,199 +238,104 @@ public class TablistManager implements Listener {
String uuid = player.getUniqueId().toString(); String uuid = player.getUniqueId().toString();
String nickname = nicknameConfig.getString(uuid); String nickname = nicknameConfig.getString(uuid);
if (nickname != null && !nickname.trim().isEmpty()) { if (nickname != null && !nickname.trim().isEmpty()) {
try { return translateNickColors(nickname);
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".nickname", nickname);
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return nickname;
} }
} catch (Exception ignored) { } catch (Exception ignored) {}
// Keine Konsolenausgabe
}
return null; return null;
} }
/** /**
* Nametag über dem Kopf aktualisieren * Übersetzt &-Codes und Hex-Farben
*/ */
private void updateNametag(Player player, String prefix, String displayName) { private String translateNickColors(String input) {
String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input);
Matcher matcher = HEX_PATTERN.matcher(withLegacy);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String hexCode = matcher.group();
String replacement = ChatColor.of(hexCode).toString();
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* Aktualisiert den Nametag über dem Kopf genau wie in deinem alten Code:
* - Wenn Nick vorhanden: Nick als Prefix (zeigt [Nick] Spielername)
* - Sonst Rang-Prefix (oder [Spieler] wenn kein Rang)
* Der Entry ist dabei immer der echte Spielername.
*/
private void updateNametag(Player player, String rankPrefix, String nickname) {
if (scoreboard == null) return; if (scoreboard == null) return;
try { try {
String teamName = generateTeamName(player, prefix); String teamName = generateTeamName(player, rankPrefix);
Team team = scoreboard.getTeam(teamName); Team team = scoreboard.getTeam(teamName);
// Team erstellen oder aktualisieren
if (team == null) { if (team == null) {
team = scoreboard.registerNewTeam(teamName); // register new team if not exists
try {
team = scoreboard.registerNewTeam(teamName);
} catch (IllegalArgumentException ignored) {
team = scoreboard.getTeam(teamName);
}
}
if (team == null) return;
String prefix;
if (nickname != null && !nickname.trim().isEmpty()) {
prefix = nickname;
} else {
String rp = (rankPrefix != null && !rankPrefix.trim().isEmpty()) ? rankPrefix : "&7[&aSpieler&7]&f ";
prefix = color(rp);
} }
// Prefix zwingend setzen, wenn LuckPerms installiert ist try {
String coloredPrefix = color(prefix != null && !prefix.trim().isEmpty() ? prefix : (luckPerms != null ? "&7[Spieler] " : "")); team.setPrefix(prefix);
team.setPrefix(coloredPrefix); } catch (Exception ignored) {}
// Ensure the genuine player name is used as the team entry
String entry = player.getName();
// Spieler dem Team hinzufügen
String entry = displayName != null && !displayName.trim().isEmpty() ? displayName : player.getName();
if (!team.hasEntry(entry)) { if (!team.hasEntry(entry)) {
// Spieler aus anderen Teams entfernen, um Konflikte zu vermeiden // Remove from other teams to avoid duplicates
for (Team existingTeam : scoreboard.getTeams()) { for (Team existingTeam : scoreboard.getTeams()) {
if (!existingTeam.getName().equals(teamName)) { if (existingTeam != null && !existingTeam.getName().equals(teamName)) {
existingTeam.removeEntry(entry); try {
existingTeam.removeEntry(player.getName()); existingTeam.removeEntry(entry);
} catch (Exception ignored) {}
} }
} }
team.addEntry(entry); try {
team.addEntry(entry);
} catch (Exception ignored) {}
} }
// Team für alle Spieler sichtbar machen // Synchronize scoreboard to online players (use main scoreboard)
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) { for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (onlinePlayer.getScoreboard() != scoreboard) { try {
onlinePlayer.setScoreboard(scoreboard); if (onlinePlayer.getScoreboard() != scoreboard) {
} onlinePlayer.setScoreboard(scoreboard);
}
} catch (Exception ignored) {}
} }
} catch (Exception ignored) {}
// Debug-Ausgabe für Nametag
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".nametag_prefix", coloredPrefix);
debugConfig.set(player.getUniqueId().toString() + ".nametag_entry", entry);
debugConfig.save(debugFile);
} catch (Exception ignored) {}
} catch (Exception ignored) {
// Keine Konsolenausgabe
}
} }
/** /**
* Eindeutigen Teamnamen generieren * Eindeutigen Teamnamen generieren
*/ */
private String generateTeamName(Player player, String prefix) { private String generateTeamName(Player player, String prefix) {
// Verwende UUID für eindeutige Teamnamen, falls kein Prefix vorhanden
if (prefix == null || prefix.trim().isEmpty()) { if (prefix == null || prefix.trim().isEmpty()) {
return "nametag_" + player.getUniqueId().toString().substring(0, 8); return "nametag_" + player.getUniqueId().toString().substring(0, 8);
} }
// Verwende Prefix für Teamnamen, max 16 Zeichen (Bukkit-Beschränkung)
String sanitizedPrefix = prefix.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); String sanitizedPrefix = prefix.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
return "prefix_" + sanitizedPrefix.substring(0, Math.min(sanitizedPrefix.length(), 12)); return "prefix_" + sanitizedPrefix.substring(0, Math.min(sanitizedPrefix.length(), 12));
} }
/** /**
* Spieler-Prefix abrufen (LuckPerms primär, PlaceholderAPI als Fallback) * Versucht, Adventure Components zu verwenden (falls verfügbar).
*/
private String getPlayerPrefix(Player player) {
// Wenn LuckPerms installiert ist, Prefix zwingend abrufen
if (luckPerms != null) {
// Versuche LuckPerms-API zuerst
try {
User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);
String prefix = user.getCachedData().getMetaData().getPrefix();
if (prefix != null && !prefix.trim().isEmpty()) {
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".prefix", prefix);
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return prefix + " ";
}
} catch (Exception ignored) {
// Keine Konsolenausgabe
}
// Fallback auf PlaceholderAPI, wenn LuckPerms installiert ist
if (hasPlaceholderAPI) {
try {
String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
if (prefix != null && !prefix.trim().isEmpty()) {
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".prefix", prefix);
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return prefix + " ";
}
} catch (Exception ignored) {
// Keine Konsolenausgabe
}
}
// Standard-Prefix, wenn LuckPerms installiert ist, aber kein Prefix definiert
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".prefix", "&7[Spieler]");
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return "&7[Spieler] ";
}
// Wenn LuckPerms nicht installiert ist, aber PlaceholderAPI vorhanden ist
if (hasPlaceholderAPI) {
try {
String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
if (prefix != null && !prefix.trim().isEmpty()) {
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".prefix", prefix);
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return prefix + " ";
}
} catch (Exception ignored) {
// Keine Konsolenausgabe
}
}
// Standard-Prefix, wenn weder LuckPerms noch PlaceholderAPI einen Prefix liefern
try {
File debugFile = new File(plugin.getDataFolder(), "debug.yml");
FileConfiguration debugConfig = YamlConfiguration.loadConfiguration(debugFile);
debugConfig.set(player.getUniqueId().toString() + ".prefix", "&7[Spieler]");
debugConfig.save(debugFile);
} catch (Exception ignored) {}
return "&7[Spieler] ";
}
/**
* Spieler-Ping abrufen
*/
private int getPlayerPing(Player player) {
try {
Method getHandle = player.getClass().getMethod("getHandle");
Object entityPlayer = getHandle.invoke(player);
return entityPlayer.getClass().getField("ping").getInt(entityPlayer);
} catch (Exception ignored) {
return -1; // Keine Konsolenausgabe
}
}
/**
* Chat-Format modifizieren
*/
@EventHandler
public void onPlayerChat(AsyncPlayerChatEvent event) {
try {
Player player = event.getPlayer();
String displayName = getNickname(player);
if (displayName == null || displayName.trim().isEmpty()) {
displayName = player.getName();
}
String prefix = getPlayerPrefix(player);
String format = color(prefix + displayName + "&7: &f") + "%2$s";
event.setFormat(format);
} catch (Exception ignored) {
// Keine Konsolenausgabe
}
}
/**
* Adventure-Variante mit Components
*/ */
private boolean tryAdventureComponent(Player player, String headerRaw, String footerRaw) { private boolean tryAdventureComponent(Player player, String headerRaw, String footerRaw) {
try { try {
@@ -443,7 +356,6 @@ public class TablistManager implements Listener {
headerComp = textMethod.invoke(null, color(headerRaw)); headerComp = textMethod.invoke(null, color(headerRaw));
footerComp = textMethod.invoke(null, color(footerRaw)); footerComp = textMethod.invoke(null, color(footerRaw));
} else { } else {
// Fallback: MiniMessage
try { try {
Class<?> miniMsgClass = Class.forName("net.kyori.adventure.text.minimessage.MiniMessage"); Class<?> miniMsgClass = Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
Method miniMsgSingleton = miniMsgClass.getMethod("miniMessage"); Method miniMsgSingleton = miniMsgClass.getMethod("miniMessage");
@@ -451,9 +363,7 @@ public class TablistManager implements Listener {
Method deserialize = miniMsgClass.getMethod("deserialize", String.class); Method deserialize = miniMsgClass.getMethod("deserialize", String.class);
headerComp = deserialize.invoke(miniMsg, headerRaw); headerComp = deserialize.invoke(miniMsg, headerRaw);
footerComp = deserialize.invoke(miniMsg, footerRaw); footerComp = deserialize.invoke(miniMsg, footerRaw);
} catch (Exception ignored) { } catch (Exception ignored) {}
// Kein MiniMessage
}
} }
if (headerComp == null || footerComp == null) return false; if (headerComp == null || footerComp == null) return false;
@@ -474,13 +384,13 @@ public class TablistManager implements Listener {
} }
} }
} catch (Exception ignored) { } catch (Exception ignored) {
return false; // fallback to string method
} }
return false; return false;
} }
/** /**
* String-Variante * Fallback: konventionelle String-Header/Footer mittels Reflection
*/ */
private boolean tryStringMethod(Player player, String header, String footer) { private boolean tryStringMethod(Player player, String header, String footer) {
try { try {
@@ -500,15 +410,10 @@ public class TablistManager implements Listener {
mf.invoke(player, footer); mf.invoke(player, footer);
return true; return true;
} }
} catch (Exception ignored) { } catch (Exception ignored) {}
return false;
}
return false; return false;
} }
/**
* Hilfsmethode für Reflection
*/
private Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { private Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
Class<?> current = clazz; Class<?> current = clazz;
while (current != null) { while (current != null) {
@@ -522,8 +427,67 @@ public class TablistManager implements Listener {
} }
/** /**
* Farbcode-Konvertierung (& -> §) * Holt den Rang-Prefix (LuckPerms -> PlaceholderAPI -> Default [Spieler])
*/ */
private String getPlayerPrefix(Player player) {
if (luckPerms != null) {
try {
User user = luckPerms.getPlayerAdapter(Player.class).getUser(player);
String prefix = user.getCachedData().getMetaData().getPrefix();
if (prefix != null && !prefix.trim().isEmpty()) {
return prefix + " ";
}
} catch (Exception ignored) {}
if (hasPlaceholderAPI) {
try {
String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
if (prefix != null && !prefix.trim().isEmpty()) {
return prefix + " ";
}
} catch (Exception ignored) {}
}
return "&7[&aSpieler&7]&f ";
}
if (hasPlaceholderAPI) {
try {
String prefix = PlaceholderAPI.setPlaceholders(player, "%luckperms_prefix%");
if (prefix != null && !prefix.trim().isEmpty()) {
return prefix + " ";
}
} catch (Exception ignored) {}
}
return "&7[&aSpieler&7]&f ";
}
private int getPlayerPing(Player player) {
try {
Method getHandle = player.getClass().getMethod("getHandle");
Object entityPlayer = getHandle.invoke(player);
return entityPlayer.getClass().getField("ping").getInt(entityPlayer);
} catch (Exception ignored) {
return -1;
}
}
/**
* Chat-Format (Nickname wird hier angezeigt). Setzt Prefix + Nick/Name im Chat.
*/
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerChat(AsyncPlayerChatEvent event) {
try {
Player player = event.getPlayer();
String displayName = getNickname(player);
if (displayName == null || displayName.trim().isEmpty()) {
displayName = player.getName();
}
String prefix = getPlayerPrefix(player);
// Format: Rang + Nick + ": " + Nachricht
String format = color(prefix + displayName + "&7: &f") + "%2$s";
event.setFormat(format);
} catch (Exception ignored) {}
}
private String color(String msg) { private String color(String msg) {
if (msg == null) return ""; if (msg == null) return "";
return msg.replace("&", "§"); return msg.replace("&", "§");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,141 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location; // <--- WICHTIG: Das war der Fehler
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class AdminToolsCommand implements CommandExecutor, Listener {
private final SurvivalPlus plugin;
private final Set<UUID> vanishedPlayers = new HashSet<>();
private final Set<UUID> frozenPlayers = new HashSet<>();
public AdminToolsCommand(SurvivalPlus plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Nur Spieler können diesen Befehl nutzen!");
return true;
}
Player player = (Player) sender;
FileConfiguration lang = plugin.getLangConfig();
// --- VANISH ---
if (command.getName().equalsIgnoreCase("vanish")) {
if (!player.hasPermission("survivalplus.vanish")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (vanishedPlayers.contains(player.getUniqueId())) {
// Ent-vanish
for (Player all : Bukkit.getOnlinePlayers()) {
all.showPlayer(plugin, player);
}
player.setPlayerListName(player.getName()); // Name wieder in Tab-Liste anzeigen
vanishedPlayers.remove(player.getUniqueId());
player.sendMessage(ChatColor.GREEN + "Du bist nun sichtbar.");
} else {
// Vanish
for (Player all : Bukkit.getOnlinePlayers()) {
all.hidePlayer(plugin, player);
}
vanishedPlayers.add(player.getUniqueId());
player.sendMessage(ChatColor.GREEN + "Du bist nun unsichtbar.");
}
return true;
}
// --- FREEZE ---
if (command.getName().equalsIgnoreCase("freeze")) {
if (!player.hasPermission("survivalplus.freeze")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length == 0) {
player.sendMessage(ChatColor.RED + "Verwendung: /freeze <Spieler>");
return true;
}
Player target = Bukkit.getPlayer(args[0]);
if (target == null) {
player.sendMessage(ChatColor.RED + "Spieler nicht gefunden!");
return true;
}
if (frozenPlayers.contains(target.getUniqueId())) {
// Ent-freezen
frozenPlayers.remove(target.getUniqueId());
player.sendMessage(ChatColor.GREEN + target.getName() + " wurde aufgetaut.");
target.sendMessage(ChatColor.GREEN + "Du bist nicht mehr eingefroren.");
} else {
// Einfrieren
frozenPlayers.add(target.getUniqueId());
player.sendMessage(ChatColor.GREEN + target.getName() + " wurde eingefroren.");
target.sendMessage(ChatColor.RED + "Du wurdest von einem Admin eingefroren! " + ChatColor.BOLD + "Keine Bewegung möglich!");
}
return true;
}
return false;
}
// --- EVENTS ---
// Verhindert Bewegung wenn eingefroren
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
// Kopf drehen (YAW/PITCH) ist okay, Blocken wenn Position (X/Y/Z) ändert
if (frozenPlayers.contains(player.getUniqueId())) {
Location from = event.getFrom();
Location to = event.getTo();
// Wenn die Koordinaten sich ändern
if (to.getX() != from.getX() || to.getY() != from.getY() || to.getZ() != from.getZ()) {
event.setCancelled(true);
// Optional: Kleine Meldung beim Versuch zu laufen
// player.sendMessage(ChatColor.RED + "Du bist eingefroren!");
}
}
}
// Wenn ein Spieler joint: Hide vanished players for him
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player joiner = event.getPlayer();
for (UUID vanishedUUID : vanishedPlayers) {
Player vanishedPlayer = Bukkit.getPlayer(vanishedUUID);
if (vanishedPlayer != null && vanishedPlayer.isOnline()) {
joiner.hidePlayer(plugin, vanishedPlayer);
}
}
}
// Cleanup wenn ein Spieler geht
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
vanishedPlayers.remove(event.getPlayer().getUniqueId());
frozenPlayers.remove(event.getPlayer().getUniqueId());
}
}

View File

@@ -2,9 +2,12 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.Manager.BlockManager; import de.viper.survivalplus.Manager.BlockManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.*; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
public class BlockCommand implements CommandExecutor { public class BlockCommand implements CommandExecutor {
@@ -20,28 +23,40 @@ public class BlockCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
sender.sendMessage(config.getString("messages.general.only_players")); sender.sendMessage(color(config.getString("messages.general.only_players", "§cDieser Befehl ist nur für Spieler!")));
return true;
}
if (!player.hasPermission("survivalplus.block")) {
player.sendMessage(color(config.getString("messages.general.no_permission", "§cDu hast keine Berechtigung für diesen Befehl!")));
return true; return true;
} }
if (args.length != 1) { if (args.length != 1) {
player.sendMessage(config.getString("messages.block.usage")); player.sendMessage(color(config.getString("messages.block.usage", "§cVerwendung: /block <Spieler>")));
return true; return true;
} }
Player target = Bukkit.getPlayerExact(args[0]); Player target = Bukkit.getPlayerExact(args[0]);
if (target == null || target == player) { if (target == null || target == player) {
player.sendMessage(config.getString("messages.block.invalid_player")); player.sendMessage(color(config.getString("messages.block.invalid_player", "§cDieser Spieler konnte nicht gefunden werden oder bist du selbst.")));
return true; return true;
} }
if (blockManager.hasBlocked(player, target)) { if (blockManager.hasBlocked(player, target)) {
player.sendMessage(config.getString("messages.block.already_blocked").replace("%player%", target.getName())); String msg = config.getString("messages.block.already_blocked", "&cDieser Spieler ist bereits blockiert!");
player.sendMessage(color(msg.replace("%player%", target.getName())));
} else { } else {
blockManager.blockPlayer(player, target); blockManager.blockPlayer(player, target);
player.sendMessage(config.getString("messages.block.blocked").replace("%player%", target.getName())); String msg = config.getString("messages.block.blocked", "&aDu hast %player% blockiert.");
player.sendMessage(color(msg.replace("%player%", target.getName())));
} }
return true; return true;
} }
private String color(String msg) {
if (msg == null) return "";
return ChatColor.translateAlternateColorCodes('&', msg);
}
} }

View File

@@ -2,9 +2,12 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.Manager.BlockManager; import de.viper.survivalplus.Manager.BlockManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.*; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -22,22 +25,30 @@ public class BlockListCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
sender.sendMessage(config.getString("messages.general.only_players")); // Fallback: "Nur Spieler..." falls Key fehlt
sender.sendMessage(color(config.getString("messages.general.only_players", "§cNur Spieler können diesen Befehl ausführen!")));
return true; return true;
} }
var blocked = blockManager.getBlockedPlayers(player); var blocked = blockManager.getBlockedPlayers(player);
if (blocked.isEmpty()) { if (blocked.isEmpty()) {
player.sendMessage(config.getString("messages.blocklist.no_blocked_players")); player.sendMessage(color(config.getString("messages.blocklist.no_blocked_players", "§cDu hast keine Spieler blockiert.")));
return true; return true;
} }
String list = blocked.stream() String list = blocked.stream()
.map(Bukkit::getOfflinePlayer) .map(Bukkit::getOfflinePlayer)
.map(p -> p.getName() != null ? p.getName() : "Unbekannt") .map(p -> p.getName() != null ? p.getName() : "§eUnbekannt")
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
player.sendMessage(config.getString("messages.blocklist.blocked_players").replace("%list%", list)); // WICHTIG: Default-String als zweiten Parameter, um NullPointerException zu vermeiden
String rawMessage = config.getString("messages.blocklist.blocked_players", "&eBlockierte Spieler: %list%");
player.sendMessage(color(rawMessage.replace("%list%", list)));
return true; return true;
} }
private String color(String msg) {
if (msg == null) return "";
return ChatColor.translateAlternateColorCodes('&', msg);
}
} }

View File

@@ -0,0 +1,408 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.util.Claim;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;
public class ClaimCommand implements CommandExecutor {
private SurvivalPlus plugin;
private static final int MAX_CLAIMS = 10;
private static final int MAX_AREA = 10000; // 100x100 blocks
public ClaimCommand(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(plugin.getMessage("commands.player-only"));
return true;
}
Player player = (Player) sender;
if (args.length == 0) {
if (!player.hasPermission("survivalplus.claim.use")) {
player.sendMessage(plugin.getMessage("commands.no-permission"));
return true;
}
Location point1 = plugin.getPoint1(player);
Location point2 = plugin.getPoint2(player);
if (point1 == null || point2 == null) {
player.sendMessage(plugin.getMessage("claim.points-not-set"));
return true;
}
if (!point1.getWorld().equals(point2.getWorld())) {
player.sendMessage(plugin.getMessage("claim.different-worlds"));
return true;
}
int x1 = point1.getBlockX();
int z1 = point1.getBlockZ();
int x2 = point2.getBlockX();
int z2 = point2.getBlockZ();
int width = Math.abs(x2 - x1) + 1;
int length = Math.abs(z2 - z1) + 1;
if (width * length > MAX_AREA) {
player.sendMessage(plugin.getMessage("claim.too-large").replace("%max%", String.valueOf(MAX_AREA)));
return true;
}
Claim newClaim = new Claim(player, point1.getWorld().getName(), x1, z1, x2, z2);
if (plugin.hasOverlap(newClaim)) {
player.sendMessage(plugin.getMessage("claim.overlap"));
return true;
}
if (plugin.getClaimCount(player) >= MAX_CLAIMS) {
player.sendMessage(plugin.getMessage("claim.max-reached").replace("%max%", String.valueOf(MAX_CLAIMS)));
return true;
}
plugin.addClaim(newClaim, player);
player.sendMessage(plugin.getMessage("claim.success").replace("%count%", String.valueOf(plugin.getClaimCount(player))).replace("%max%", String.valueOf(MAX_CLAIMS)));
plugin.clearSelections(player);
return true;
}
switch (args[0].toLowerCase()) {
case "mark": {
if (!player.hasPermission("survivalplus.claim.use")) {
player.sendMessage(plugin.getMessage("commands.no-permission"));
return true;
}
if (args.length < 2 || (!args[1].equals("1") && !args[1].equals("2"))) {
player.sendMessage(plugin.getMessage("claim.usage-mark"));
return true;
}
Location location = player.getLocation();
if (args[1].equals("1")) {
plugin.setPoint1(player, location);
player.sendMessage(plugin.getMessage("claim.point1-set").replace("%x%", String.valueOf(location.getBlockX())).replace("%z%", String.valueOf(location.getBlockZ())));
} else {
plugin.setPoint2(player, location);
player.sendMessage(plugin.getMessage("claim.point2-set").replace("%x%", String.valueOf(location.getBlockX())).replace("%z%", String.valueOf(location.getBlockZ())));
}
return true;
}
case "unclaim":
case "del":
case "delete": {
// 1. Mass Delete (Admin): /claim unclaim <Spieler> (oder del/delete)
if (args.length == 2 && player.isOp()) {
Player target = Bukkit.getPlayer(args[1]);
UUID targetUUID;
if (target != null) {
targetUUID = target.getUniqueId();
} else {
try {
targetUUID = UUID.fromString(args[1]);
} catch (IllegalArgumentException e) {
player.sendMessage(plugin.getMessage("commands.player-not-found"));
return true;
}
}
int removedCount = removePlayerClaims(targetUUID);
if (removedCount > 0) {
player.sendMessage(plugin.getMessage("claim.op-unclaimed")
.replace("%player%", args[1])
.replace("%count%", String.valueOf(removedCount)));
} else {
player.sendMessage(plugin.getMessage("claim.no-claims-found")
.replace("%player%", args[1]));
}
return true;
}
// 2. Normaler oder Admin Targeted Delete
Claim claim = plugin.getClaim(player.getLocation());
if (claim == null) {
player.sendMessage(plugin.getMessage("claim.no-claim-at-location"));
return true;
}
// Prüfen, ob Spieler Owner ist (oder OP)
if (!claim.getOwner().equals(player.getUniqueId())) {
// NICHT Owner -> Aber Admin?
if (player.isOp()) {
plugin.deleteClaim(claim);
String ownerName = Bukkit.getOfflinePlayer(claim.getOwner()).getName();
if (ownerName == null) ownerName = "Unbekannt";
player.sendMessage(ChatColor.GREEN + "Claim wurde entfernt. (Besitzer war: " + ChatColor.YELLOW + ownerName + ChatColor.GREEN + ")");
return true;
} else {
// Nicht Owner und kein Admin -> Abbruch
player.sendMessage(plugin.getMessage("claim.not-owner"));
return true;
}
}
// Owner löscht eigenen Claim
plugin.deleteClaim(claim);
player.sendMessage(plugin.getMessage("claim.unclaimed"));
return true;
}
case "trust": {
if (!player.hasPermission("survivalplus.claim.trust")) {
player.sendMessage(plugin.getMessage("commands.no-permission"));
return true;
}
if (args.length < 2) {
player.sendMessage(plugin.getMessage("claim.usage-trust"));
return true;
}
Player target = Bukkit.getPlayer(args[1]);
if (target == null) {
player.sendMessage(plugin.getMessage("commands.player-not-found"));
return true;
}
Claim trustClaim = plugin.getClaim(player.getLocation());
if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(plugin.getMessage("claim.not-owner"));
return true;
}
trustClaim.addTrusted(target.getUniqueId());
player.sendMessage(plugin.getMessage("claim.trusted").replace("%player%", args[1]));
plugin.saveClaims();
return true;
}
case "untrust": {
if (!player.hasPermission("survivalplus.claim.trust")) {
player.sendMessage(plugin.getMessage("commands.no-permission"));
return true;
}
if (args.length < 2) {
player.sendMessage(plugin.getMessage("claim.usage-untrust"));
return true;
}
Player target = Bukkit.getPlayer(args[1]);
if (target == null) {
player.sendMessage(plugin.getMessage("commands.player-not-found"));
return true;
}
Claim trustClaim = plugin.getClaim(player.getLocation());
if (trustClaim == null || !trustClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(plugin.getMessage("claim.not-owner"));
return true;
}
trustClaim.removeTrusted(target.getUniqueId());
player.sendMessage(plugin.getMessage("claim.untrusted").replace("%player%", args[1]));
plugin.saveClaims();
return true;
}
// --- NEU: KICK ---
case "kick": {
if (!player.hasPermission("survivalplus.claim.kick")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim kick <Spieler>");
return true;
}
Player kickTarget = Bukkit.getPlayer(args[1]);
if (kickTarget == null) {
player.sendMessage(ChatColor.RED + "Spieler nicht gefunden oder offline!");
return true;
}
Claim currentClaim = plugin.getClaim(kickTarget.getLocation());
if (currentClaim == null || !currentClaim.getOwner().equals(player.getUniqueId())) {
player.sendMessage(ChatColor.RED + "Du bist hier nicht Besitzer oder kein Claim.");
return true;
}
kickPlayerFromClaim(kickTarget, currentClaim);
player.sendMessage(ChatColor.GREEN + "Spieler " + kickTarget.getName() + " wurde aus dem Claim gekickt!");
kickTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen!");
return true;
}
// --- NEU: BAN ---
case "ban": {
if (!player.hasPermission("survivalplus.claim.ban")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim ban <Spieler>");
return true;
}
String claimKey = getClaimKeyAtLocation(player.getLocation());
if (claimKey == null) {
player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim.");
return true;
}
Player banTarget = Bukkit.getPlayer(args[1]);
UUID banUUID;
if (banTarget != null) {
banUUID = banTarget.getUniqueId();
Claim banCheck = plugin.getClaim(player.getLocation());
if (banCheck != null && banCheck.isInside(banTarget.getLocation())) {
kickPlayerFromClaim(banTarget, banCheck);
banTarget.sendMessage(ChatColor.RED + "Du wurdest aus dem Claim geworfen und gebannt!");
}
} else {
try {
banUUID = UUID.fromString(args[1]);
} catch (IllegalArgumentException e) {
player.sendMessage(ChatColor.RED + "Ungültige UUID oder Spieler nicht online.");
return true;
}
}
banPlayerInClaim(banUUID, claimKey);
player.sendMessage(ChatColor.GREEN + "Spieler wurde von diesem Claim gebannt.");
return true;
}
// --- NEU: UNBAN ---
case "unban": {
if (!player.hasPermission("survivalplus.claim.ban")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
if (args.length < 2) {
player.sendMessage(ChatColor.RED + "Verwendung: /claim unban <Spieler>");
return true;
}
Player unbanTarget = Bukkit.getPlayer(args[1]);
UUID unbanUUID;
if (unbanTarget != null) {
unbanUUID = unbanTarget.getUniqueId();
} else {
try {
unbanUUID = UUID.fromString(args[1]);
} catch (IllegalArgumentException e) {
player.sendMessage(ChatColor.RED + "Ungültige UUID.");
return true;
}
}
String claimKeyUnban = getClaimKeyAtLocation(player.getLocation());
if (claimKeyUnban == null) {
player.sendMessage(ChatColor.RED + "Du stehst in keinem Claim.");
return true;
}
unbanPlayerInClaim(unbanUUID, claimKeyUnban);
player.sendMessage(ChatColor.GREEN + "Spieler wurde für diesen Claim entbannt.");
return true;
}
case "info": {
if (!player.hasPermission("survivalplus.claim.use")) {
player.sendMessage(plugin.getMessage("commands.no-permission"));
return true;
}
Claim infoClaim = plugin.getClaim(player.getLocation());
if (infoClaim == null) {
player.sendMessage(plugin.getMessage("claim.no-claim-at-location"));
return true;
}
String ownerName = Bukkit.getOfflinePlayer(infoClaim.getOwner()).getName();
if (ownerName == null) {
ownerName = infoClaim.getOwner().toString();
}
player.sendMessage(plugin.getMessage("claim.info")
.replace("%owner%", ownerName)
.replace("%world%", infoClaim.getWorldName())
.replace("%x1%", String.valueOf(infoClaim.getX1()))
.replace("%z1%", String.valueOf(infoClaim.getZ1()))
.replace("%x2%", String.valueOf(infoClaim.getX2()))
.replace("%z2%", String.valueOf(infoClaim.getZ2())));
return true;
}
default:
player.sendMessage(plugin.getMessage("claim.usage"));
}
return true;
}
private int removePlayerClaims(UUID targetUUID) {
Map<String, Claim> claims = new HashMap<>(plugin.getClaims());
int removedCount = 0;
for (Map.Entry<String, Claim> entry : claims.entrySet()) {
if (entry.getValue().getOwner().equals(targetUUID)) {
plugin.removeClaimByKey(entry.getKey(), targetUUID);
removedCount++;
}
}
if (removedCount > 0) {
plugin.saveClaims();
}
return removedCount;
}
// --- HILFSMETHODEN ---
private void kickPlayerFromClaim(Player player, Claim claim) {
Location loc = player.getLocation();
int x = loc.getBlockX();
int z = loc.getBlockZ();
int minX = Math.min(claim.getX1(), claim.getX2());
int maxX = Math.max(claim.getX1(), claim.getX2());
int minZ = Math.min(claim.getZ1(), claim.getZ2());
int maxZ = Math.max(claim.getZ1(), claim.getZ2());
int distX1 = Math.abs(x - minX);
int distX2 = Math.abs(x - maxX);
int distZ1 = Math.abs(z - minZ);
int distZ2 = Math.abs(z - maxZ);
int minDist = Math.min(Math.min(distX1, distX2), Math.min(distZ1, distZ2));
Location newLoc = loc.clone();
if (minDist == distX1) {
newLoc.setX(minX - 1);
} else if (minDist == distX2) {
newLoc.setX(maxX + 1);
} else if (minDist == distZ1) {
newLoc.setZ(minZ - 1);
} else {
newLoc.setZ(maxZ + 1);
}
newLoc.setY(newLoc.getY() + 1);
player.teleport(newLoc);
}
private String getClaimKeyAtLocation(Location loc) {
for (String key : plugin.getClaims().keySet()) {
if (plugin.getClaims().get(key).isInside(loc)) {
return key;
}
}
return null;
}
private void banPlayerInClaim(UUID uuid, String claimKey) {
FileConfiguration config = plugin.getClaimsConfig();
List<String> banned = config.getStringList(claimKey + ".banned");
if (!banned.contains(uuid.toString())) {
banned.add(uuid.toString());
config.set(claimKey + ".banned", banned);
plugin.saveClaims();
}
}
private void unbanPlayerInClaim(UUID uuid, String claimKey) {
FileConfiguration config = plugin.getClaimsConfig();
List<String> banned = config.getStringList(claimKey + ".banned");
banned.remove(uuid.toString());
config.set(claimKey + ".banned", banned);
plugin.saveClaims();
}
}

View File

@@ -116,14 +116,14 @@ public class FriendCommand implements CommandExecutor {
} }
private void sendHelpMessage(Player player) { private void sendHelpMessage(Player player) {
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.header", "&6=== Freundesliste Hilfe ==="))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.header", "&6== Freundesliste Hilfe ==")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.add", "&e/friend add <Spielername> &7- Freundschaftsanfrage senden"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.add", "&e/friend add <Spielername> &7- Freundschaftsanfrage senden")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.accept", "&e/friend accept <Spielername> &7- Freundschaftsanfrage akzeptieren"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.accept", "&e/friend accept <Spielername> &7- Freundschaftsanfrage akzeptieren")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.deny", "&e/friend deny <Spielername> &7- Freundschaftsanfrage ablehnen"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.deny", "&e/friend deny <Spielername> &7- Freundschaftsanfrage ablehnen")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.list", "&e/friend list &7- Liste deiner Freunde anzeigen"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.list", "&e/friend list &7- Liste deiner Freunde anzeigen")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.del", "&e/friend del <Spielername> &7- Freund aus der Liste entfernen"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.del", "&e/friend del <Spielername> &7- Freund aus der Liste entfernen")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.tp", "&e/friend tp <Spielername> &7- Zu einem Freund teleportieren"))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.tp", "&e/friend tp <Spielername> &7- Zu einem Freund teleportieren")));
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.footer", "&6====================="))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.help.footer", "&6=======================")));
} }
private void handleFriendAdd(Player player, String targetName) { private void handleFriendAdd(Player player, String targetName) {
@@ -155,15 +155,23 @@ public class FriendCommand implements CommandExecutor {
pendingRequests.add(playerUUID.toString()); pendingRequests.add(playerUUID.toString());
friendsConfig.set(targetUUID + ".pending_requests", pendingRequests); friendsConfig.set(targetUUID + ".pending_requests", pendingRequests);
friendsConfig.set(targetUUID + ".name", targetName); friendsConfig.set(targetUUID + ".name", targetName);
// --- FIX START: Absenderdaten speichern, damit er auch offline gefunden werden kann ---
friendsConfig.set(playerUUID + ".name", player.getName());
friendsConfig.set(playerUUID + ".last-online", System.currentTimeMillis());
// --- FIX END ---
saveFriendsConfig(); saveFriendsConfig();
player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.sent", "&aFreundschaftsanfrage an %s gesendet!").replace("%s", targetName))); player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.sent", "&aFreundschaftsanfrage an %s gesendet!").replace("%s", targetName)));
TextComponent message = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.received", "&aDu hast eine Freundschaftsanfrage von %s erhalten! ").replace("%s", player.getName()))); TextComponent message = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.received", "&aDu hast eine Freundschaftsanfrage von %s erhalten! ").replace("%s", player.getName())));
TextComponent accept = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.accept-button", "&a[Accept]"))); TextComponent accept = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.accept-button", "&a[Accept]")));
accept.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend accept " + player.getName())); // ÄNDERUNG: RUN_COMMAND -> SUGGEST_COMMAND
accept.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/friend accept " + player.getName()));
TextComponent deny = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.deny-button", "&c [Deny]"))); TextComponent deny = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.add.deny-button", "&c [Deny]")));
deny.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend deny " + player.getName())); // ÄNDERUNG: RUN_COMMAND -> SUGGEST_COMMAND
deny.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/friend deny " + player.getName()));
message.addExtra(accept); message.addExtra(accept);
message.addExtra(deny); message.addExtra(deny);
target.spigot().sendMessage(message); target.spigot().sendMessage(message);
@@ -268,7 +276,8 @@ public class FriendCommand implements CommandExecutor {
} }
TextComponent removeButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.remove-button", "&c[X]"))); TextComponent removeButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.list.remove-button", "&c[X]")));
removeButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend del " + friendName)); // ÄNDERUNG: RUN_COMMAND -> SUGGEST_COMMAND
removeButton.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/friend del " + friendName));
entry.addExtra(" "); entry.addExtra(" ");
entry.addExtra(removeButton); entry.addExtra(removeButton);
@@ -296,9 +305,11 @@ public class FriendCommand implements CommandExecutor {
TextComponent confirmMessage = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm", "&cMöchtest du %s wirklich aus deiner Freundesliste entfernen? ").replace("%s", friendName))); TextComponent confirmMessage = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm", "&cMöchtest du %s wirklich aus deiner Freundesliste entfernen? ").replace("%s", friendName)));
TextComponent confirmButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm-button", "&a[Confirm]"))); TextComponent confirmButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.confirm-button", "&a[Confirm]")));
confirmButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend confirm " + friendName)); // ÄNDERUNG: RUN_COMMAND -> SUGGEST_COMMAND
confirmButton.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/friend confirm " + friendName));
TextComponent cancelButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.cancel-button", "&c[Cancel]"))); TextComponent cancelButton = new TextComponent(ChatColor.translateAlternateColorCodes('&', langConfig.getString("friend.del.cancel-button", "&c[Cancel]")));
cancelButton.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/friend list")); // ÄNDERUNG: RUN_COMMAND -> SUGGEST_COMMAND
cancelButton.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/friend list"));
confirmMessage.addExtra(confirmButton); confirmMessage.addExtra(confirmButton);
confirmMessage.addExtra(" "); confirmMessage.addExtra(" ");
confirmMessage.addExtra(cancelButton); confirmMessage.addExtra(cancelButton);

View File

@@ -0,0 +1,58 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
public class HeadCommand implements CommandExecutor {
private SurvivalPlus plugin;
public HeadCommand(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Nur Spieler können diesen Befehl nutzen.");
return true;
}
Player player = (Player) sender;
if (args.length == 0) {
player.sendMessage(plugin.getMessage("commands.player-only"));
return true;
}
if (!player.hasPermission("survivalplus.head")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung!");
return true;
}
String targetName = args[0];
OfflinePlayer target = Bukkit.getOfflinePlayer(targetName);
if (target == null) {
player.sendMessage(ChatColor.RED + "Spieler '" + targetName + "' wurde nie gefunden.");
return true;
}
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) head.getItemMeta();
meta.setOwningPlayer(target);
meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + targetName);
head.setItemMeta(meta);
player.getInventory().addItem(head);
player.sendMessage(ChatColor.GREEN + "Du hast den Kopf von " + targetName + " erhalten!");
return true;
}
}

View File

@@ -34,10 +34,25 @@ public class NickCommand implements CommandExecutor {
if (args.length == 0) { if (args.length == 0) {
player.sendMessage("§eBenutzung: /nick <Name>"); player.sendMessage("§eBenutzung: /nick <Name>");
player.sendMessage("§eVerwende /nick off um den Nickname zu entfernen.");
return true; return true;
} }
// Name zusammensetzen // Entfernen des Nicks
if (args.length == 1 && (args[0].equalsIgnoreCase("off") || args[0].equalsIgnoreCase("remove") || args[0].equalsIgnoreCase("reset"))) {
String uuid = player.getUniqueId().toString();
plugin.getNicknamesConfig().set(uuid, null);
plugin.saveNicknamesConfig();
// WICHTIG: Nur DisplayName setzen, TablistManager übernimmt den Rest
player.setDisplayName(player.getName());
player.sendMessage("§aDein Nickname wurde entfernt.");
return true;
}
// Zusammenbauen des Nicknames
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (String arg : args) { for (String arg : args) {
sb.append(arg).append(" "); sb.append(arg).append(" ");
@@ -56,28 +71,24 @@ public class NickCommand implements CommandExecutor {
// Farben anwenden // Farben anwenden
String coloredNick = translateColors(rawNick); String coloredNick = translateColors(rawNick);
// Format: [Nickname] (Klammern weiß, Nickname farbig) // Chat/Display: [Nick]
String finalNick = "§f[" + coloredNick + "§f]"; String finalNickForDisplay = "§f[" + coloredNick + "§f]";
// Anwenden // WICHTIG: Nur DisplayName setzen
player.setDisplayName(finalNick); // playerListName wird vom TablistManager automatisch gesetzt
player.setPlayerListName(finalNick); player.setDisplayName(finalNickForDisplay);
// Speichern (ungefärbten Namen) // Speichern (ungefärbten Namen)
plugin.getNicknamesConfig().set(player.getUniqueId().toString(), rawNick); plugin.getNicknamesConfig().set(player.getUniqueId().toString(), rawNick);
plugin.saveNicknamesConfig(); plugin.saveNicknamesConfig();
player.sendMessage("§aDein Nickname wurde zu " + finalNick + " geändert."); player.sendMessage("§aDein Nickname wurde zu " + finalNickForDisplay + " geändert.");
return true; return true;
} }
private String translateColors(String input) { private String translateColors(String input) {
String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input); String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input);
return replaceHexColors(withLegacy); Matcher matcher = HEX_PATTERN.matcher(withLegacy);
}
private String replaceHexColors(String input) {
Matcher matcher = HEX_PATTERN.matcher(input);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
while (matcher.find()) { while (matcher.find()) {
String hexCode = matcher.group(); String hexCode = matcher.group();

View File

@@ -30,20 +30,32 @@ public class PluginCommand implements CommandExecutor {
// /sp oder /sp help // /sp oder /sp help
if (args.length == 0 || args[0].equalsIgnoreCase("help")) { if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
String mode = "all";
int page = 1; int page = 1;
if (args.length > 1 && args[0].equalsIgnoreCase("help")) { if (args.length > 1 && args[0].equalsIgnoreCase("help")) {
try { String arg1 = args[1];
page = Integer.parseInt(args[1]); if (isInteger(arg1)) {
if (page < 1) page = 1; page = Integer.parseInt(arg1);
} catch (NumberFormatException e) { } else {
sender.sendMessage(color(lang.getString( mode = arg1.toLowerCase();
"sp.invalid-subcommand", if (args.length > 2) {
"§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]" String arg2 = args[2];
))); if (isInteger(arg2)) {
return true; page = Integer.parseInt(arg2);
} else {
sender.sendMessage(color(lang.getString(
"sp.invalid-subcommand",
"§cUngültiger Unterbefehl! Verwendung: /sp help [player|admin|system|all] [seite]"
)));
return true;
}
}
} }
} }
showHelp(sender, page);
if (page < 1) page = 1;
showHelp(sender, mode, page);
return true; return true;
} }
@@ -175,7 +187,7 @@ public class PluginCommand implements CommandExecutor {
return true; return true;
} }
private void showHelp(CommandSender sender, int page) { private void showHelp(CommandSender sender, String mode, int page) {
FileConfiguration help = plugin.getHelpConfig(); FileConfiguration help = plugin.getHelpConfig();
FileConfiguration lang = plugin.getLangConfig(); FileConfiguration lang = plugin.getLangConfig();
@@ -184,7 +196,30 @@ public class PluginCommand implements CommandExecutor {
return; return;
} }
List<String> commands = new ArrayList<>(help.getConfigurationSection("commands").getKeys(false)); List<String> orderedCommands = new ArrayList<>(help.getConfigurationSection("commands").getKeys(false));
if (orderedCommands.isEmpty()) {
sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar.")));
return;
}
String normalizedMode = mode == null ? "all" : mode.toLowerCase();
if (!normalizedMode.equals("all") && !normalizedMode.equals("player")
&& !normalizedMode.equals("admin") && !normalizedMode.equals("system")) {
sender.sendMessage(color(help.getString(
"help-usage",
"&cVerwendung: /sp help [player|admin|system|all] [seite]"
)));
return;
}
List<String> commands = new ArrayList<>();
for (String key : orderedCommands) {
String group = help.getString("commands." + key + ".group", "player").toLowerCase();
if (normalizedMode.equals("all") || group.equals(normalizedMode)) {
commands.add(key);
}
}
if (commands.isEmpty()) { if (commands.isEmpty()) {
sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar."))); sender.sendMessage(color(lang.getString("sp.help-not-found", "&cKeine Befehle verfügbar.")));
return; return;
@@ -199,19 +234,30 @@ public class PluginCommand implements CommandExecutor {
int startIndex = (page - 1) * COMMANDS_PER_PAGE; int startIndex = (page - 1) * COMMANDS_PER_PAGE;
int endIndex = Math.min(startIndex + COMMANDS_PER_PAGE, commands.size()); int endIndex = Math.min(startIndex + COMMANDS_PER_PAGE, commands.size());
String lastGroup = null;
for (int i = startIndex; i < endIndex; i++) { for (int i = startIndex; i < endIndex; i++) {
String cmd = commands.get(i); String cmd = commands.get(i);
String group = help.getString("commands." + cmd + ".group", "player").toLowerCase();
if (lastGroup == null || !lastGroup.equals(group)) {
String groupTitle = help.getString("groups." + group + ".title", "&6-- " + group + " --");
sender.sendMessage(color(groupTitle));
lastGroup = group;
}
String usage = help.getString("commands." + cmd + ".usage", ""); String usage = help.getString("commands." + cmd + ".usage", "");
String description = help.getString("commands." + cmd + ".description", ""); String description = help.getString("commands." + cmd + ".description", "");
sender.sendMessage(color(usage + " §7- " + description)); sender.sendMessage(color(usage + " §7- " + description));
} }
if (sender instanceof Player player) { if (sender instanceof Player player) {
String modeSuffix = normalizedMode.equals("all") ? "" : " " + normalizedMode;
String navBase = "/sp help" + modeSuffix + " ";
TextComponent navigation = new TextComponent(); TextComponent navigation = new TextComponent();
if (page > 1) { if (page > 1) {
TextComponent prev = new TextComponent(color(help.getString("navigation.prev", "« Vorherige "))); TextComponent prev = new TextComponent(color(help.getString("navigation.prev", "« Vorherige ")));
prev.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page - 1))); prev.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, navBase + (page - 1)));
prev.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder("§7Klicke für Seite " + (page - 1)).create()));
navigation.addExtra(prev); navigation.addExtra(prev);
} else { } else {
navigation.addExtra(color(help.getString("navigation.prev-disabled", "« "))); navigation.addExtra(color(help.getString("navigation.prev-disabled", "« ")));
@@ -224,7 +270,9 @@ public class PluginCommand implements CommandExecutor {
if (page < totalPages) { if (page < totalPages) {
TextComponent next = new TextComponent(color(help.getString("navigation.next", " Nächste »"))); TextComponent next = new TextComponent(color(help.getString("navigation.next", " Nächste »")));
next.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/sp help " + (page + 1))); next.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, navBase + (page + 1)));
next.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
new ComponentBuilder("§7Klicke für Seite " + (page + 1)).create()));
navigation.addExtra(next); navigation.addExtra(next);
} else { } else {
navigation.addExtra(color(help.getString("navigation.next-disabled", " »"))); navigation.addExtra(color(help.getString("navigation.next-disabled", " »")));
@@ -242,6 +290,18 @@ public class PluginCommand implements CommandExecutor {
sender.sendMessage(color(help.getString("footer", "&7=========================="))); sender.sendMessage(color(help.getString("footer", "&7==========================")));
} }
private boolean isInteger(String value) {
if (value == null || value.isEmpty()) {
return false;
}
for (int i = 0; i < value.length(); i++) {
if (!Character.isDigit(value.charAt(i))) {
return false;
}
}
return true;
}
private String color(String input) { private String color(String input) {
return ChatColor.translateAlternateColorCodes('&', input == null ? "" : input); return ChatColor.translateAlternateColorCodes('&', input == null ? "" : input);
} }

View File

@@ -0,0 +1,100 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.util.RayTraceResult;
public class RideCommand implements CommandExecutor {
private final SurvivalPlus plugin;
public RideCommand(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage("Dieser Befehl ist nur für Spieler!");
return true;
}
Player player = (Player) sender;
FileConfiguration lang = plugin.getLangConfig();
if (!player.hasPermission("survivalplus.ride")) {
player.sendMessage(ChatColor.RED + "Du hast keine Berechtigung dafür!");
return true;
}
// --- FALL 1: ABSTEIGEN ---
if (args.length == 0) {
if (player.isInsideVehicle()) {
player.leaveVehicle();
player.sendMessage(ChatColor.GREEN + "Du bist abgestiegen!");
return true;
}
// --- FALL 2: AUFSTEIGEN (Via Raycast) ---
double maxDistance = 5.0;
// FIX: Wir übergeben einen Filter, der den Spieler selbst ignoriert
RayTraceResult rayTrace = player.getWorld().rayTraceEntities(
player.getEyeLocation(),
player.getEyeLocation().getDirection(),
maxDistance,
1.0, // Ray Größe (Dicke des Strahls)
entity -> !entity.getUniqueId().equals(player.getUniqueId()) // WICHTIG: Ignoriere mich selbst
);
if (rayTrace == null || rayTrace.getHitEntity() == null) {
player.sendMessage(ChatColor.RED + "Du schaust auf nichts zum Reiten!");
player.sendMessage(ChatColor.GRAY + "Schaue einen Spieler oder Mob an.");
return true;
}
Entity target = rayTrace.getHitEntity();
// Verhindern, dass man aufgesammelte Items reitet
if (target instanceof Item) {
player.sendMessage(ChatColor.RED + "Du kannst keine aufgesammelten Items reiten!");
return true;
}
// --- Zielle Logik ---
if (target instanceof Player) {
Player targetPlayer = (Player) target;
if (targetPlayer.equals(player)) {
player.sendMessage(ChatColor.RED + "Du kannst dich nicht selbst reiten!");
return true;
}
if (targetPlayer.hasPermission("survivalplus.ride.exempt")) {
player.sendMessage(ChatColor.RED + "Du kannst diesen Spieler nicht reiten!");
return true;
}
target.addPassenger(player);
player.sendMessage(ChatColor.GREEN + "Du reitest nun " + ChatColor.YELLOW + targetPlayer.getName() + ChatColor.GREEN + "!");
targetPlayer.sendMessage(ChatColor.GOLD + player.getName() + ChatColor.GREEN + " reitet dich jetzt!");
} else {
target.addPassenger(player);
player.sendMessage(ChatColor.GREEN + "Du reitest nun ein(e) " + target.getName() + "!");
}
return true;
}
player.sendMessage(ChatColor.GRAY + "Benutzung: Schaue auf ein Wesen und tippe " + ChatColor.WHITE + "/ride");
return true;
}
}

View File

@@ -11,15 +11,33 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.plugin.RegisteredServiceProvider;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
public class ShopCommand implements CommandExecutor { public class ShopCommand implements CommandExecutor {
private final ShopManager shopManager; private final ShopManager shopManager;
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private Economy economy;
public ShopCommand(SurvivalPlus plugin) { public ShopCommand(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
this.shopManager = new ShopManager(plugin); this.shopManager = new ShopManager(plugin);
// Economy laden (für Admin-Verkauf an Server)
try {
if (Bukkit.getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Economy> rsp = Bukkit.getServicesManager().getRegistration(Economy.class);
if (rsp != null) {
this.economy = rsp.getProvider();
plugin.getLogger().info("Vault Economy für ShopCommand gefunden.");
}
}
} catch (Exception e) {
// Ignorieren wenn Vault fehlt
}
} }
private String getMessage(String key) { private String getMessage(String key) {
@@ -34,51 +52,103 @@ public class ShopCommand implements CommandExecutor {
} }
Player player = (Player) sender; Player player = (Player) sender;
// Shop-GUI öffnen, wenn kein Argument oder "gui" // Shop-GUI öffnen
if (args.length == 0 || (args.length > 0 && args[0].toLowerCase().equals("gui"))) { if (args.length == 0 || (args.length > 0 && args[0].equalsIgnoreCase("gui"))) {
ShopGui shopGui = new ShopGui(plugin, player, shopManager); ShopGui shopGui = new ShopGui(plugin, player, shopManager);
Bukkit.getPluginManager().registerEvents(shopGui, plugin); Bukkit.getPluginManager().registerEvents(shopGui, plugin);
player.openInventory(shopGui.getInventory()); player.openInventory(shopGui.getInventory());
return true; return true;
} }
if (args.length < 3) { // --- SHOP ADD LOGIK ---
sender.sendMessage(getMessage("usage-add")); if (args.length >= 4 && args[0].equalsIgnoreCase("add")) {
return false;
}
if (!args[0].toLowerCase().equals("add")) { // Economy-Check
sender.sendMessage(getMessage("unknown-subcommand")); if (economy == null) {
return false; sender.sendMessage(ChatColor.RED + "Vault Economy ist nicht verfügbar!");
} return true;
}
String materialName = args[1];
String priceStr = args[2];
String amountStr = args[3];
Material mat = Material.matchMaterial(materialName);
if (mat == null) {
sender.sendMessage(ChatColor.RED + "Das Material '" + materialName + "' wurde nicht gefunden!");
return true;
}
double price;
int amount;
try {
price = Double.parseDouble(priceStr);
amount = Integer.parseInt(amountStr);
} catch (NumberFormatException e) {
sender.sendMessage(ChatColor.RED + "Ungültige Preis oder Bestandszahl! Beispiel: /shop add Diamond 20 64");
return true;
}
if (amount <= 0 || price < 0) {
sender.sendMessage(ChatColor.RED + "Menge und Preis müssen positiv sein.");
return true;
}
// 1. Prüfen: Hat der Spieler die Items?
int hasAmount = countItems(player.getInventory(), mat);
if (hasAmount < amount) {
sender.sendMessage(ChatColor.RED + "Du hast nicht genug Items! (Benötigt: " + amount + ", Vorhanden: " + hasAmount + ")");
return true;
}
// 2. Items aus Inventar entfernen
player.getInventory().removeItem(new ItemStack(mat, amount));
player.updateInventory(); // Wichtig für Client-Update
// 3. Spieler Geld geben (Verkauf an Server)
double totalCost = price * amount;
EconomyResponse response = economy.depositPlayer(player, totalCost);
if (response.transactionSuccess()) {
sender.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + mat.name() + " für " + totalCost + " Coins an den Server verkauft!");
// 4. Shop-Config aktualisieren (Lagerbestand erhöhen)
String itemKey = mat.name().toLowerCase();
shopManager.addOrUpdateItem(itemKey, price, shopManager.getStock(itemKey) + amount);
} else {
sender.sendMessage(ChatColor.RED + "Fehler bei der Auszahlung!");
return true;
}
ItemStack itemInHand = player.getInventory().getItemInMainHand();
if (itemInHand == null || itemInHand.getType() == Material.AIR) {
sender.sendMessage(ChatColor.RED + "Du musst das Item, das du hinzufügen willst, in der Hand halten!");
return true; return true;
} }
String itemKey = itemInHand.getType().name().toLowerCase(); // Fallback für falsche Argumente
if (args.length > 0 && args[0].equalsIgnoreCase("add")) {
double basePrice; sender.sendMessage(ChatColor.RED + "Falsche Benutzung!");
int stock; sender.sendMessage(ChatColor.GRAY + "Mit Item in Hand: /shop add <Preis> <Bestand>");
sender.sendMessage(ChatColor.GRAY + "Mit Namen: /shop add <Material> <Preis> <Bestand>");
try { return true;
basePrice = Double.parseDouble(args[1]);
stock = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
sender.sendMessage(getMessage("number-error"));
return false;
} }
shopManager.addOrUpdateItem(itemKey, basePrice, stock); sender.sendMessage(ChatColor.RED + "Unbekannter Befehl! Benutze /shop [add|gui]");
String msg = getMessage("item-added")
.replace("{item}", itemKey)
.replace("{price}", args[1])
.replace("{stock}", args[2]);
sender.sendMessage(msg);
return true; return true;
} }
/**
* Zählt die Anzahl der Items eines bestimmten Materials im Inventar.
* Nutzt StorageContents, um auch Rüstung/Shulker-Slots zu prüfen.
*/
private int countItems(PlayerInventory inv, Material mat) {
int count = 0;
for (ItemStack item : inv.getStorageContents()) {
if (item != null && item.getType().equals(mat)) {
count += item.getAmount();
}
}
return count;
}
} }

View File

@@ -2,7 +2,10 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.listeners.SitListener; import de.viper.survivalplus.listeners.SitListener;
import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -38,20 +41,50 @@ public class SitCommand implements CommandExecutor {
return true; return true;
} }
// Prüfe, ob der Spieler bereits sitzt // --- ÄNDERUNG: Wenn man schon sitzt, tue nichts ---
if (sitListener.isSitting(player)) { if (sitListener.isSitting(player)) {
sitListener.standUp(player); player.sendMessage(ChatColor.RED + "Du sitzt bereits!");
player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!")); return true;
}
// --------------------------------------------------
if (player.isInsideVehicle()) {
player.sendMessage(lang.getString("sit.already-in-vehicle", "§cDu kannst dich nicht setzen, während du in einem Fahrzeug bist!"));
return true; return true;
} }
// Setze den Spieler mittig auf den Block und leicht oberhalb (Fix) // Prüfe den Boden unter dem Spieler
Location loc = player.getLocation();
Block blockBelow = loc.clone().subtract(0, 1, 0).getBlock();
if (blockBelow.getType().isAir() || !blockBelow.getType().isSolid()) {
player.sendMessage(plugin.getLangConfig().getString("sit.no-solid-ground", "§cDu kannst dich hier nicht hinsetzen (kein fester Boden)."));
return true;
}
// Position vorbereiten
Location location = player.getLocation(); Location location = player.getLocation();
location.setX(location.getBlockX() + 0.5); location.setX(location.getBlockX() + 0.5);
location.setY(location.getBlockY() + 0.25);
location.setZ(location.getBlockZ() + 0.5); location.setZ(location.getBlockZ() + 0.5);
// Höhe dynamisch berechnen
double y = location.getBlockY();
if (isStair(blockBelow.getType()) || isSlab(blockBelow.getType())) {
y += 0.5;
}
location.setY(y);
sitListener.sitPlayer(player, location); sitListener.sitPlayer(player, location);
return true; return true;
} }
private boolean isStair(Material material) {
return material.name().endsWith("_STAIRS");
}
private boolean isSlab(Material material) {
return material.name().endsWith("_SLAB") || material.name().endsWith("_STEP") || material.name().equals("PRISMARINE_SLAB");
}
} }

View File

@@ -0,0 +1,369 @@
package de.viper.survivalplus.commands;
import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.Manager.Warp;
import de.viper.survivalplus.Manager.WarpManager;
import de.viper.survivalplus.fun.FunChallenge;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
public class SurvivalPlusTabCompleter implements TabCompleter {
private static final List<String> SP_SUBCOMMANDS = List.of(
"help", "reload", "info", "share", "shareconfirm", "sharecancel", "cb",
"lock", "unlock", "friendadd", "friendremove"
);
private static final List<String> SP_CB_SUBCOMMANDS = List.of("add", "remove", "list");
private static final List<String> FRIEND_SUBCOMMANDS = List.of("add", "accept", "deny", "list", "del", "confirm", "tp");
private static final List<String> CLAIM_SUBCOMMANDS = List.of("mark", "unclaim", "del", "delete", "trust", "untrust", "info", "kick", "ban", "unban");
private static final List<String> SPLOCK_SUBCOMMANDS = List.of("lock", "unlock", "friendadd", "friendremove");
private static final List<String> SHOP_SUBCOMMANDS = List.of("gui", "add");
private static final List<String> NICK_SUBCOMMANDS = List.of("off", "remove", "reset");
private static final List<String> GAMEMODE_SUBCOMMANDS = List.of("0", "1", "2", "3", "survival", "creative", "adventure", "spectator");
private final SurvivalPlus plugin;
public SurvivalPlusTabCompleter(SurvivalPlus plugin) {
this.plugin = plugin;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
String cmd = command.getName().toLowerCase(Locale.ROOT);
if (args.length == 0) {
return Collections.emptyList();
}
switch (cmd) {
case "sp":
return completeSp(sender, args);
case "friend":
return completeFriend(sender, args);
case "claim":
return completeClaim(sender, args);
case "splock":
return completeSplock(sender, args);
case "shop":
return completeShop(sender, args);
case "nick":
return filterPrefix(NICK_SUBCOMMANDS, args[0]);
case "gm":
return completeGamemode(sender, args);
case "tp":
case "tphere":
case "tpa":
case "trade":
case "tradeaccept":
case "report":
case "showreport":
case "clearreport":
case "block":
case "unblock":
case "head":
case "freeze":
return completePlayerArg(sender, args);
case "heal":
case "inv":
case "ec":
case "ride":
return completeOptionalPlayerArg(sender, args);
case "tploot":
return completeTploot(args);
case "startchallenge":
return completeChallenges(args);
case "delwarp":
return completePlayerWarpNames(sender, args);
case "home":
case "delhome":
return completePlayerHomeNames(sender, args);
default:
return Collections.emptyList();
}
}
private List<String> completeSp(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SP_SUBCOMMANDS, args[0]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("cb")) {
return filterPrefix(SP_CB_SUBCOMMANDS, args[1]);
}
if (args.length == 3 && args[0].equalsIgnoreCase("cb") && args[1].equalsIgnoreCase("remove")) {
return filterPrefix(getBlockedCommands(), args[2]);
}
if (args.length == 2 && (args[0].equalsIgnoreCase("friendadd") || args[0].equalsIgnoreCase("friendremove"))) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
return Collections.emptyList();
}
private List<String> completeFriend(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(FRIEND_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("add") || sub.equals("accept") || sub.equals("deny") || sub.equals("tp")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
if (sub.equals("del") || sub.equals("confirm")) {
return filterPrefix(getFriendNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeClaim(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(CLAIM_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("mark")) {
return filterPrefix(List.of("1", "2"), args[1]);
}
if (sub.equals("trust") || sub.equals("untrust") || sub.equals("kick") || sub.equals("ban") || sub.equals("unban")
|| sub.equals("unclaim") || sub.equals("del") || sub.equals("delete")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeSplock(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SPLOCK_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
String sub = args[0].toLowerCase(Locale.ROOT);
if (sub.equals("friendadd") || sub.equals("friendremove")) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
}
return Collections.emptyList();
}
private List<String> completeShop(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(SHOP_SUBCOMMANDS, args[0]);
}
if (args.length == 2 && args[0].equalsIgnoreCase("add")) {
String prefix = args[1];
if (prefix.length() < 2) {
return Collections.emptyList();
}
return getMaterialNames(prefix);
}
return Collections.emptyList();
}
private List<String> completeGamemode(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(GAMEMODE_SUBCOMMANDS, args[0]);
}
if (args.length == 2) {
return filterPrefix(getOnlinePlayerNames(sender), args[1]);
}
return Collections.emptyList();
}
private List<String> completePlayerArg(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getOnlinePlayerNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completeOptionalPlayerArg(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getOnlinePlayerNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completeTploot(String[] args) {
if (args.length == 1) {
return filterPrefix(getWorldNames(), args[0]);
}
return Collections.emptyList();
}
private List<String> completeChallenges(String[] args) {
if (args.length == 1) {
return filterPrefix(getChallengeNames(), args[0]);
}
return Collections.emptyList();
}
private List<String> completePlayerHomeNames(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getHomeNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> completePlayerWarpNames(CommandSender sender, String[] args) {
if (args.length == 1) {
return filterPrefix(getWarpNames(sender), args[0]);
}
return Collections.emptyList();
}
private List<String> getOnlinePlayerNames(CommandSender sender) {
List<String> names = new ArrayList<>();
for (Player player : Bukkit.getOnlinePlayers()) {
if (!player.getName().equalsIgnoreCase(sender.getName())) {
names.add(player.getName());
}
}
return names;
}
private List<String> getBlockedCommands() {
FileConfiguration blocked = plugin.getBlockedCommandsConfig();
if (blocked == null) {
return Collections.emptyList();
}
List<String> commands = blocked.getStringList("blocked-commands");
return commands == null ? Collections.emptyList() : commands;
}
private List<String> getFriendNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
FileConfiguration friends = plugin.getFriendsConfig();
if (friends == null) {
return Collections.emptyList();
}
List<String> friendIds = friends.getStringList(player.getUniqueId().toString() + ".friends");
if (friendIds == null || friendIds.isEmpty()) {
return Collections.emptyList();
}
List<String> names = new ArrayList<>();
for (String id : friendIds) {
try {
UUID uuid = UUID.fromString(id);
OfflinePlayer offline = Bukkit.getOfflinePlayer(uuid);
if (offline.getName() != null) {
names.add(offline.getName());
continue;
}
String storedName = friends.getString(id + ".name");
if (storedName != null && !storedName.isEmpty()) {
names.add(storedName);
}
} catch (IllegalArgumentException ignored) {
}
}
return names;
}
private List<String> getHomeNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
FileConfiguration homes = plugin.getHomesConfig();
if (homes == null) {
return Collections.emptyList();
}
String path = "homes." + player.getUniqueId();
if (homes.getConfigurationSection(path) == null) {
return Collections.emptyList();
}
return new ArrayList<>(homes.getConfigurationSection(path).getKeys(false));
}
private List<String> getWarpNames(CommandSender sender) {
if (!(sender instanceof Player player)) {
return Collections.emptyList();
}
WarpManager warpManager = plugin.getWarpManager();
if (warpManager == null) {
return Collections.emptyList();
}
List<String> warps = new ArrayList<>();
for (Warp warp : warpManager.getWarps().values()) {
if (warp.getOwner().equalsIgnoreCase(player.getName())) {
warps.add(warp.getName());
}
}
return warps;
}
private List<String> getChallengeNames() {
if (plugin.getFunChallengeManager() == null) {
return Collections.emptyList();
}
List<String> names = new ArrayList<>();
for (FunChallenge challenge : plugin.getFunChallengeManager().getChallenges()) {
names.add(challenge.getName());
}
return names;
}
private List<String> getWorldNames() {
List<String> names = new ArrayList<>();
for (World world : Bukkit.getWorlds()) {
names.add(world.getName());
}
return names;
}
private List<String> getMaterialNames(String prefix) {
List<String> names = new ArrayList<>();
String normalized = prefix == null ? "" : prefix.toUpperCase(Locale.ROOT);
for (Material material : Material.values()) {
if (!material.isItem() || material == Material.AIR) {
continue;
}
String name = material.name();
if (name.startsWith(normalized)) {
names.add(name);
}
}
Collections.sort(names);
if (names.size() > 25) {
return new ArrayList<>(names.subList(0, 25));
}
return names;
}
private List<String> filterPrefix(Collection<String> options, String prefix) {
if (options == null || options.isEmpty()) {
return Collections.emptyList();
}
String normalized = prefix == null ? "" : prefix.toLowerCase(Locale.ROOT);
List<String> results = new ArrayList<>();
Set<String> dedup = new HashSet<>();
for (String option : options) {
if (option == null) {
continue;
}
String value = option.toString();
if (value.toLowerCase(Locale.ROOT).startsWith(normalized)) {
if (dedup.add(value)) {
results.add(value);
}
}
}
Collections.sort(results);
return results;
}
}

View File

@@ -2,9 +2,12 @@ package de.viper.survivalplus.commands;
import de.viper.survivalplus.Manager.BlockManager; import de.viper.survivalplus.Manager.BlockManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.*; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
public class UnblockCommand implements CommandExecutor { public class UnblockCommand implements CommandExecutor {
@@ -20,29 +23,40 @@ public class UnblockCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player player)) { if (!(sender instanceof Player player)) {
sender.sendMessage(config.getString("messages.general.only_players")); sender.sendMessage(color(config.getString("messages.general.only_players", "§cDieser Befehl ist nur für Spieler!")));
return true;
}
if (!player.hasPermission("survivalplus.unblock")) {
player.sendMessage(color(config.getString("messages.general.no_permission", "§cDu hast keine Berechtigung für diesen Befehl!")));
return true; return true;
} }
if (args.length != 1) { if (args.length != 1) {
player.sendMessage(config.getString("messages.unblock.usage")); player.sendMessage(color(config.getString("messages.block.usage", "§cVerwendung: /unblock <Spieler>")));
return true; return true;
} }
Player target = Bukkit.getPlayerExact(args[0]); Player target = Bukkit.getPlayerExact(args[0]);
if (target == null || target == player) { if (target == null || target == player) {
player.sendMessage(config.getString("messages.unblock.invalid_player")); player.sendMessage(color(config.getString("messages.block.invalid_player", "§cDieser Spieler konnte nicht gefunden werden oder bist du selbst.")));
return true; return true;
} }
if (blockManager.hasBlocked(player, target)) { if (!blockManager.hasBlocked(player, target)) {
blockManager.unblockPlayer(player, target); String msg = config.getString("messages.block.not_blocked", "&cDieser Spieler ist nicht blockiert.");
player.sendMessage(config.getString("messages.unblock.unblocked").replace("%player%", target.getName())); player.sendMessage(color(msg.replace("%player%", target.getName())));
target.sendMessage(config.getString("messages.unblock.unblocked_by").replace("%player%", player.getName()));
} else { } else {
player.sendMessage(config.getString("messages.unblock.not_blocked").replace("%player%", target.getName())); blockManager.unblockPlayer(player, target);
String msg = config.getString("messages.block.unblocked", "&aDu hast %player% entblockt.");
player.sendMessage(color(msg.replace("%player%", target.getName())));
} }
return true; return true;
} }
private String color(String msg) {
if (msg == null) return "";
return ChatColor.translateAlternateColorCodes('&', msg);
}
} }

View File

@@ -42,8 +42,7 @@ public class ShopGui implements Listener {
for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) { for (String itemKey : shopManager.getShopConfig().getConfigurationSection("items").getKeys(false)) {
Material mat = getMaterialFromKey(itemKey); Material mat = getMaterialFromKey(itemKey);
if (mat == null) { if (mat == null) {
player.sendMessage(ChatColor.RED + "Material für '" + itemKey + "' konnte nicht gefunden werden."); continue; // Unbekannte Materialien einfach überspringen
continue;
} }
addShopItem(itemKey, mat, 64); addShopItem(itemKey, mat, 64);
@@ -64,6 +63,7 @@ public class ShopGui implements Listener {
private void addShopItem(String itemKey, Material material, int amount) { private void addShopItem(String itemKey, Material material, int amount) {
double pricePerUnit = shopManager.getCurrentPrice(itemKey); double pricePerUnit = shopManager.getCurrentPrice(itemKey);
int currentStock = shopManager.getStock(itemKey);
double totalPrice = pricePerUnit * amount; double totalPrice = pricePerUnit * amount;
ItemStack item = new ItemStack(material, amount); ItemStack item = new ItemStack(material, amount);
@@ -71,9 +71,12 @@ public class ShopGui implements Listener {
if (meta != null) { if (meta != null) {
meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name()); meta.setDisplayName(ChatColor.GREEN.toString() + amount + "x " + material.name());
meta.setLore(Arrays.asList( meta.setLore(Arrays.asList(
ChatColor.GRAY + "Lagerbestand: " + ChatColor.YELLOW + currentStock,
ChatColor.YELLOW + "Preis pro Stück: " + pricePerUnit, ChatColor.YELLOW + "Preis pro Stück: " + pricePerUnit,
ChatColor.YELLOW + "Gesamtpreis: " + totalPrice, ChatColor.YELLOW + "Gesamtpreis: " + totalPrice,
ChatColor.GRAY + "Klicke, um zu kaufen" "",
ChatColor.GREEN + "Linksklick zum Kaufen",
ChatColor.RED + "Rechtsklick zum Verkaufen"
)); ));
item.setItemMeta(meta); item.setItemMeta(meta);
} }
@@ -101,25 +104,54 @@ public class ShopGui implements Listener {
Material mat = clicked.getType(); Material mat = clicked.getType();
String itemKey = mat.name().toLowerCase(); String itemKey = mat.name().toLowerCase();
if (!shopManager.buyItem(itemKey, amount)) { if (e.isLeftClick()) {
player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop."); // --- KAUFEN ---
return; if (!shopManager.buyItem(itemKey, amount)) {
player.sendMessage(ChatColor.RED + "Nicht genügend Bestand im Shop.");
return;
}
double totalPrice = shopManager.getCurrentPrice(itemKey) * amount;
// TODO: Economy Abzug hier einfügen!
// Beispiel: if (!economy.withdrawPlayer(player, totalPrice)) { ... }
player.getInventory().addItem(new ItemStack(mat, amount));
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + String.format("%.2f", totalPrice) + " gekauft.");
} else if (e.isRightClick()) {
// --- VERKAUFEN ---
// Prüfen ob Spieler genug Items hat
if (!player.getInventory().containsAtLeast(new ItemStack(mat), amount)) {
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items zum Verkaufen.");
return;
}
// Verkaufslogik
shopManager.sellItem(itemKey, amount);
double sellPrice = shopManager.getCurrentPrice(itemKey) * amount; // Neuer Preis nach dem Verkauf wird genutzt
// Hinweis: In echten Systemen bekommt man oft den Preis *vor* dem Preisverfall, hier nehmen wir den neuen für Einfachheit oder den gespeicherten:
// Besser wäre: double oldPrice = price * amount; ... player.giveMoney(oldPrice);
// TODO: Economy Gutschrift hier einfügen!
player.getInventory().removeItem(new ItemStack(mat, amount));
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + String.format("%.2f", sellPrice) + " verkauft.");
} }
double totalPrice = shopManager.getCurrentPrice(itemKey) * amount; // Inventory schließen, um Preise neu zu laden und Bugs zu vermeiden
// TODO: Economy Abzug einfügen
player.getInventory().addItem(new ItemStack(mat, amount));
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + "x " + mat.name() + " für " + totalPrice + " Coins gekauft.");
player.closeInventory(); player.closeInventory();
// Optional: direkt neu öffnen mit createInventory() wenn es flüssig sein soll
// createInventory();
} }
@EventHandler @EventHandler
public void onInventoryClose(InventoryCloseEvent e) { public void onInventoryClose(InventoryCloseEvent e) {
if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) { if (e.getView().getTitle().equals("Shop") && e.getPlayer().equals(player)) {
// Optional: Listener entfernen oder Aufräumarbeiten hier // Listener kann entladen werden, wenn nicht mehr gebraucht
// HandlerList.unregisterAll(this); // Vorsicht: falls Singleton, sonst fliegt allen der GUI weg
} }
} }

View File

@@ -0,0 +1,122 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.ChatColor;
import org.bukkit.attribute.Attribute;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import java.util.UUID;
public class AdaptiveMobListener implements Listener {
private final SurvivalPlus plugin;
// Konfiguration für die Anpassung
private final double SCALING_FACTOR = 0.1; // 10% mehr Stärke pro Level
private final int KILLS_PER_LEVEL = 5; // Alle 5 Kills steigt das Level
private final int MAX_LEVEL = 50; // Maximales Level (Cap)
public AdaptiveMobListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
// 1. Verhalten tracken: Zählt Kills des Spielers
@EventHandler
public void onMobDeath(EntityDeathEvent event) {
if (event.getEntity().getKiller() instanceof Player) {
Player killer = event.getEntity().getKiller();
UUID uuid = killer.getUniqueId();
FileConfiguration mobConfig = plugin.getMobAdaptConfig();
String uuidPath = uuid.toString();
int currentLevel = mobConfig.getInt(uuidPath + ".level", 0);
int currentKills = mobConfig.getInt(uuidPath + ".kills", 0);
currentKills++;
// Level up wenn genug Kills
if (currentKills >= KILLS_PER_LEVEL && currentLevel < MAX_LEVEL) {
currentLevel++;
currentKills = 0;
killer.sendMessage(ChatColor.GREEN + "[SurvivalPlus] " + ChatColor.GRAY + "Die Monster werden aggressiver! (Threat Level: " + currentLevel + ")");
}
// Speichern
mobConfig.set(uuidPath + ".level", currentLevel);
mobConfig.set(uuidPath + ".kills", currentKills);
plugin.saveMobAdaptConfig();
}
}
// 2. Anpassung: Monster werden beim Spawnen angepasst
@EventHandler
public void onCreatureSpawn(CreatureSpawnEvent event) {
// Nur aktivieren, wenn in Config gewünscht
if (!plugin.getConfig().getBoolean("adaptive-mobs.enabled", true)) return;
// Wir greifen nur bei natürlichen Spawns ein (oder Spawn-Eiern), aber nicht bei Spawner-Blöcken (Farmen schützen)
if (event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.NATURAL &&
event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.SPAWNER_EGG &&
event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.DISPENSE_EGG) {
return;
}
LivingEntity entity = event.getEntity();
// Keine Anpassung für friedliche Tiere
if (entity instanceof Animals || entity instanceof Ambient) return;
// Nächsten Spieler suchen (Radius: 30 Blöcke)
Player nearestPlayer = null;
double minDistance = 30.0;
for (Player p : entity.getWorld().getPlayers()) {
if (p.getLocation().distance(entity.getLocation()) <= minDistance) {
nearestPlayer = p;
minDistance = p.getLocation().distance(entity.getLocation());
}
}
if (nearestPlayer != null) {
applyAttributesBasedOnPlayer(entity, nearestPlayer);
}
}
private void applyAttributesBasedOnPlayer(LivingEntity entity, Player player) {
FileConfiguration mobConfig = plugin.getMobAdaptConfig();
int level = mobConfig.getInt(player.getUniqueId().toString() + ".level", 0);
if (level == 0) return;
// Multiplikator berechnen (z.B. Level 10 -> 1.0 + (10 * 0.1) = 2.0 = doppelte Stärke)
double multiplier = 1.0 + (level * SCALING_FACTOR);
// Cap bei 5-facher Stärke, damit es nicht unspielbar wird
if (multiplier > 5.0) multiplier = 5.0;
// Lebenspunkte erhöhen
if (entity.getAttribute(Attribute.GENERIC_MAX_HEALTH) != null) {
double baseHealth = entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue();
entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(baseHealth * multiplier);
entity.setHealth(entity.getAttribute(Attribute.GENERIC_MAX_HEALTH).getValue());
}
// Angriffsschaden erhöhen
if (entity.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE) != null) {
double baseDamage = entity.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE).getBaseValue();
entity.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE).setBaseValue(baseDamage * multiplier);
}
// Optional: Visuelles Indiz (Name über dem Kopf), wenn Monster sehr stark sind
if (level >= 10) {
entity.setCustomName(ChatColor.RED + "" + ChatColor.GRAY + "Starkes Monster (Lvl " + level + ")");
entity.setCustomNameVisible(true);
}
}
}

View File

@@ -0,0 +1,132 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import de.viper.survivalplus.util.Claim;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class ClaimListener implements Listener {
private final SurvivalPlus plugin;
private final Map<UUID, Claim> lastClaim = new HashMap<>();
public ClaimListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer();
if (player.isOp()) return;
Claim claim = plugin.getClaim(event.getBlock().getLocation());
if (claim != null && !claim.getOwner().equals(player.getUniqueId()) && !claim.getTrusted().contains(player.getUniqueId())) {
event.setCancelled(true);
player.sendMessage(plugin.getMessage("claim.no-break"));
}
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
Player player = event.getPlayer();
if (player.isOp()) return;
Claim claim = plugin.getClaim(event.getBlock().getLocation());
if (claim != null && !claim.getOwner().equals(player.getUniqueId()) && !claim.getTrusted().contains(player.getUniqueId())) {
event.setCancelled(true);
player.sendMessage(plugin.getMessage("claim.no-place"));
}
}
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
UUID playerId = player.getUniqueId();
Claim currentClaim = plugin.getClaim(event.getTo());
Location to = event.getTo();
// --- NEU: BAN CHECK ---
if (currentClaim != null) {
// Checken ob Spieler in der Ban-Liste der Claims Config steht
String claimKey = getClaimKeyFromLocation(to);
if (claimKey != null) {
FileConfiguration config = plugin.getClaimsConfig();
if (config.contains(claimKey + ".banned")) {
if (config.getStringList(claimKey + ".banned").contains(playerId.toString())) {
event.setCancelled(true);
player.sendMessage(ChatColor.RED + "Du bist aus diesem Gebiet verbannt!");
// Optional: Knockback effect
player.teleport(event.getFrom().add(0, 0.2, 0));
return;
}
}
}
}
// Prüfe, ob sich der Claim-Status geändert hat
Claim previousClaim = lastClaim.getOrDefault(playerId, null);
if (currentClaim != previousClaim) {
if (currentClaim != null) {
// Spieler betritt einen Claim
String ownerName = plugin.getServer().getOfflinePlayer(currentClaim.getOwner()).getName();
if (ownerName == null) {
ownerName = currentClaim.getOwner().toString(); // Fallback auf UUID
}
player.sendMessage(plugin.getMessage("claim.enter").replace("%owner%", ownerName));
} else if (previousClaim != null) {
// Spieler verlässt einen Claim
String ownerName = plugin.getServer().getOfflinePlayer(previousClaim.getOwner()).getName();
if (ownerName == null) {
ownerName = previousClaim.getOwner().toString(); // Fallback auf UUID
}
player.sendMessage(plugin.getMessage("claim.leave").replace("%owner%", ownerName));
}
lastClaim.put(playerId, currentClaim);
}
}
// --- NEU: Schutz vor Monstern im Claim ---
@EventHandler
public void onMobTarget(EntityTargetLivingEntityEvent event) {
// 1. Prüfen, ob das Ziel ein Spieler ist
if (!(event.getTarget() instanceof Player)) return;
// 2. Prüfen, ob das Angreifer ein Monster ist (Zombie, Skelett, etc.)
if (!(event.getEntity() instanceof Monster)) return;
Player player = (Player) event.getTarget();
Location loc = player.getLocation();
// 3. Prüfen, ob der Spieler in einem Claim steht
Claim claim = plugin.getClaim(loc);
if (claim != null) {
// 4. Prüfen, ob der Spieler dort Rechte hat (Owner oder Trusted)
if (claim.getOwner().equals(player.getUniqueId()) || claim.getTrusted().contains(player.getUniqueId())) {
// Das Targeting abbrechen -> Monster greift nicht an
event.setCancelled(true);
}
}
}
// Helper für ClaimListener (Kopiert, da wir die Methode in Command nicht sehen können)
private String getClaimKeyFromLocation(Location loc) {
// Da wir hier keinen direkten Zugriff auf den Key haben, iterieren wir kurz
for (String key : plugin.getClaims().keySet()) {
if (plugin.getClaims().get(key).isInside(loc)) {
return key;
}
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
public class HeadDropListener implements Listener {
private SurvivalPlus plugin;
public HeadDropListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerDeath(PlayerDeathEvent event) {
if (!plugin.getConfig().getBoolean("heads.drop-on-death", true)) {
return;
}
Player player = event.getEntity();
ItemStack head = new ItemStack(Material.PLAYER_HEAD);
SkullMeta meta = (SkullMeta) head.getItemMeta();
meta.setOwningPlayer(player);
meta.setDisplayName(ChatColor.YELLOW + "Kopf von " + player.getName());
head.setItemMeta(meta);
event.getDrops().add(head);
}
}

View File

@@ -32,7 +32,7 @@ public class MobCapListener implements Listener {
public void reloadConfig(FileConfiguration config) { public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-cap.enabled", true); this.enabled = config.getBoolean("mob-cap.enabled", true);
this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10); this.maxAnimalsPerChunk = config.getInt("mob-cap.max-animals-per-chunk", 10);
plugin.getLogger().info("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk); plugin.logDebug("MobCapListener: enabled=" + enabled + ", maxAnimalsPerChunk=" + maxAnimalsPerChunk);
// Bereinige bestehende Daten // Bereinige bestehende Daten
chunkAnimalMap.clear(); chunkAnimalMap.clear();
@@ -40,7 +40,7 @@ public class MobCapListener implements Listener {
if (!enabled) { if (!enabled) {
plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml plugin.getMobCapConfig().set("mobcap", null); // Bereinige mobcap.yml
plugin.saveMobCapConfig(); plugin.saveMobCapConfig();
plugin.getLogger().info("MobCapListener: Daten bereinigt, da enabled=false"); plugin.logDebug("MobCapListener: Daten bereinigt, da enabled=false");
return; return;
} }
@@ -89,7 +89,7 @@ public class MobCapListener implements Listener {
// Speichere aktualisierte mobcap.yml // Speichere aktualisierte mobcap.yml
saveChunkAnimalMap(); saveChunkAnimalMap();
plugin.getLogger().info("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden."); plugin.logDebug("MobCapListener: Nach Reload " + chunkAnimalMap.size() + " Chunks mit Tieren gefunden.");
} }
@EventHandler @EventHandler
@@ -106,7 +106,7 @@ public class MobCapListener implements Listener {
int animalCount = animalToPlayer.size(); int animalCount = animalToPlayer.size();
if (animalCount >= maxAnimalsPerChunk) { if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true); event.setCancelled(true);
plugin.getLogger().info("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); plugin.logDebug("Spawn von " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
// HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent // HINWEIS: Wenn du dem Züchter eine Nachricht schicken willst, nutze EntityBreedEvent
// Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat // Hier ist das nicht möglich, weil man beim Spawn keinen Spielerzugriff hat
@@ -132,7 +132,7 @@ public class MobCapListener implements Listener {
if (animalCount >= maxAnimalsPerChunk) { if (animalCount >= maxAnimalsPerChunk) {
event.setCancelled(true); event.setCancelled(true);
player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk))); player.sendMessage(plugin.getMessage("mob-cap.limit-reached").replace("%max%", String.valueOf(maxAnimalsPerChunk)));
plugin.getLogger().info("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht."); plugin.logDebug("Interaktion mit " + entity.getType() + " in Chunk " + chunkKey + " verhindert: Limit von " + maxAnimalsPerChunk + " erreicht.");
} else { } else {
animalToPlayer.put(entity.getUniqueId(), player.getUniqueId()); animalToPlayer.put(entity.getUniqueId(), player.getUniqueId());
saveChunkAnimalMap(); saveChunkAnimalMap();
@@ -157,7 +157,7 @@ public class MobCapListener implements Listener {
chunkAnimalMap.remove(chunkKey); chunkAnimalMap.remove(chunkKey);
} }
saveChunkAnimalMap(); saveChunkAnimalMap();
plugin.getLogger().info("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size()); plugin.logDebug("Tier " + entity.getType() + " in Chunk " + chunkKey + " gestorben. Verbleibende Tiere: " + animalToPlayer.size());
} }
} }

View File

@@ -30,7 +30,7 @@ public class MobLeashLimitListener implements Listener {
public void reloadConfig(FileConfiguration config) { public void reloadConfig(FileConfiguration config) {
this.enabled = config.getBoolean("mob-leash-limit.enabled", true); this.enabled = config.getBoolean("mob-leash-limit.enabled", true);
this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5); this.maxLeashCount = config.getInt("mob-leash-limit.max-leash-count", 5);
plugin.getLogger().info("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount); plugin.logDebug("MobLeashLimitListener: enabled=" + enabled + ", maxLeashCount=" + maxLeashCount);
// Bereinige bestehende Daten // Bereinige bestehende Daten
leashedEntities.clear(); leashedEntities.clear();
@@ -40,7 +40,7 @@ public class MobLeashLimitListener implements Listener {
if (!enabled) { if (!enabled) {
plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml plugin.getLeashesConfig().set("leashes", null); // Bereinige leashes.yml
plugin.saveLeashesConfig(); plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Daten bereinigt, da enabled=false"); plugin.logDebug("MobLeashLimitListener: Daten bereinigt, da enabled=false");
return; return;
} }
@@ -119,7 +119,7 @@ public class MobLeashLimitListener implements Listener {
// Speichere aktualisierte leashes.yml // Speichere aktualisierte leashes.yml
plugin.saveLeashesConfig(); plugin.saveLeashesConfig();
plugin.getLogger().info("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden."); plugin.logDebug("MobLeashLimitListener: Nach Reload " + leashedEntities.size() + " Spieler mit angeleinten Tieren gefunden.");
} }
@EventHandler @EventHandler
@@ -135,7 +135,7 @@ public class MobLeashLimitListener implements Listener {
if (itemInHand != null && itemInHand.getType() == Material.LEAD) { if (itemInHand != null && itemInHand.getType() == Material.LEAD) {
Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>()); Set<UUID> playerLeashedEntities = leashedEntities.computeIfAbsent(playerId, k -> new HashSet<>());
int currentCount = playerLeashedEntities.size(); int currentCount = playerLeashedEntities.size();
plugin.getLogger().info("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount); plugin.logDebug("PlayerInteractEntity: player=" + player.getName() + ", currentCount=" + currentCount + ", maxLeashCount=" + maxLeashCount);
if (playerLeashedEntities.contains(entityId)) { if (playerLeashedEntities.contains(entityId)) {
// Ableinen // Ableinen

View File

@@ -26,10 +26,15 @@ public class NewbieProtectionListener implements Listener {
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private final boolean enabled; private final boolean enabled;
private final int durationMinutes; private final int durationMinutes;
private final int COOLDOWN_SECONDS = 60; // Abklingzeit für Nachrichten in Sekunden (1 Minute)
// Maps für Zeiten und BossBars // Maps für Zeiten, BossBars und Nachrichten-Abklingzeit
private final Map<UUID, Integer> remainingSeconds = new HashMap<>(); private final Map<UUID, Integer> remainingSeconds = new HashMap<>();
private final Map<UUID, BossBar> bossBars = new HashMap<>(); private final Map<UUID, BossBar> bossBars = new HashMap<>();
private final Map<UUID, Long> messageCooldowns = new HashMap<>();
// NEU: Set um zu speichern, wer schon mal gejoint ist
private final Set<UUID> hasJoined = new HashSet<>();
// YAML Datei // YAML Datei
private File dataFile; private File dataFile;
@@ -57,17 +62,23 @@ public class NewbieProtectionListener implements Listener {
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
// Falls nicht gespeichert, neue Zeit einstellen boolean isFirstJoin = !hasJoined.contains(uuid);
remainingSeconds.putIfAbsent(uuid, durationMinutes * 60);
// Bossbar erstellen/anzeigen if (isFirstJoin) {
BossBar bar = Bukkit.createBossBar( // Erster Join EVER -> Schutz geben und als "gejoint" markieren
ChatColor.GREEN + "Neulingsschutz: " + formatTime(remainingSeconds.get(uuid)), remainingSeconds.put(uuid, durationMinutes * 60);
BarColor.GREEN, hasJoined.add(uuid);
BarStyle.SOLID
); createBossBar(player, remainingSeconds.get(uuid));
bar.addPlayer(player); } else {
bossBars.put(uuid, bar); // Spieler war schonmal da -> Prüfen ob noch Schutz übrig ist
Integer timeLeft = remainingSeconds.get(uuid);
if (timeLeft != null && timeLeft > 0) {
// Schutz läuft noch -> Bossbar wiederherstellen
createBossBar(player, timeLeft);
}
// Wenn timeLeft null ist oder 0, passiert nichts -> Kein Schutz für den "alten Hasen"
}
} }
@EventHandler @EventHandler
@@ -75,12 +86,13 @@ public class NewbieProtectionListener implements Listener {
if (!enabled) return; if (!enabled) return;
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
// Bossbar entfernen, aber Zeit bleibt in Map bestehen
// Bossbar entfernen, aber Status (Zeit & Joined) in Map/Datei behalten
BossBar bar = bossBars.remove(uuid); BossBar bar = bossBars.remove(uuid);
if (bar != null) { if (bar != null) {
bar.removeAll(); bar.removeAll();
} }
saveData(); // Beim Verlassen direkt speichern saveData();
} }
@EventHandler @EventHandler
@@ -89,14 +101,27 @@ public class NewbieProtectionListener implements Listener {
if (!(event.getEntity() instanceof Player)) return; if (!(event.getEntity() instanceof Player)) return;
Player victim = (Player) event.getEntity(); Player victim = (Player) event.getEntity();
UUID vid = victim.getUniqueId(); UUID victimId = victim.getUniqueId();
Integer timeLeft = remainingSeconds.get(vid); Integer timeLeft = remainingSeconds.get(victimId);
if (timeLeft != null && timeLeft > 0) { // Nur schützen, wenn Zeit > 0
event.setCancelled(true); if (timeLeft == null || timeLeft <= 0) return;
event.setCancelled(true);
// Prüfe Cooldown für Nachrichten
long currentTime = System.currentTimeMillis() / 1000;
if (currentTime >= messageCooldowns.getOrDefault(victimId, 0L) + COOLDOWN_SECONDS) {
victim.sendMessage(ChatColor.GREEN + "Du bist noch im Neulingsschutz!"); victim.sendMessage(ChatColor.GREEN + "Du bist noch im Neulingsschutz!");
if (event.getDamager() instanceof Player) { messageCooldowns.put(victimId, currentTime);
((Player) event.getDamager()).sendMessage(ChatColor.RED + victim.getName() + " ist noch geschützt!"); }
if (event.getDamager() instanceof Player) {
Player damager = (Player) event.getDamager();
UUID damagerId = damager.getUniqueId();
if (currentTime >= messageCooldowns.getOrDefault(damagerId, 0L) + COOLDOWN_SECONDS) {
damager.sendMessage(ChatColor.RED + victim.getName() + " ist noch geschützt!");
messageCooldowns.put(damagerId, currentTime);
} }
} }
} }
@@ -109,16 +134,15 @@ public class NewbieProtectionListener implements Listener {
Player p = Bukkit.getPlayer(uuid); Player p = Bukkit.getPlayer(uuid);
if (p == null || !p.isOnline()) { if (p == null || !p.isOnline()) {
// Spieler offline Zeit pausiert // Spieler offline -> Zeit pausiert
continue; continue;
} }
int timeLeft = remainingSeconds.getOrDefault(uuid, 0); int timeLeft = remainingSeconds.getOrDefault(uuid, 0);
if (timeLeft <= 0) { if (timeLeft <= 0) {
// Ablauf: Bossbar weg + Map clean // Zeit abgelaufen -> Bossbar weg, aber aus hasJoined NICHT löschen
BossBar bar = bossBars.remove(uuid); removeProtection(uuid);
if (bar != null) bar.removeAll();
remainingSeconds.remove(uuid);
continue; continue;
} }
@@ -127,17 +151,41 @@ public class NewbieProtectionListener implements Listener {
remainingSeconds.put(uuid, timeLeft); remainingSeconds.put(uuid, timeLeft);
// Bossbar updaten // Bossbar updaten
BossBar bar = bossBars.get(uuid); updateBossBar(p, timeLeft);
if (bar != null) {
bar.setTitle(ChatColor.GREEN + "Neulingsschutz: " + formatTime(timeLeft));
bar.setProgress(Math.max(0, timeLeft / (float) (durationMinutes * 60)));
}
} }
saveData(); // zyklisch speichern // Jedes Mal zyklisch speichern
saveData();
} }
}.runTaskTimer(plugin, 20L, 20L); }.runTaskTimer(plugin, 20L, 20L);
} }
private void removeProtection(UUID uuid) {
remainingSeconds.remove(uuid);
BossBar bar = bossBars.remove(uuid);
if (bar != null) {
bar.removeAll();
}
}
private void createBossBar(Player player, int seconds) {
BossBar bar = Bukkit.createBossBar(
ChatColor.GREEN + "Neulingsschutz: " + formatTime(seconds),
BarColor.GREEN,
BarStyle.SOLID
);
bar.addPlayer(player);
bar.setProgress(Math.max(0, seconds / (float) (durationMinutes * 60)));
bossBars.put(player.getUniqueId(), bar);
}
private void updateBossBar(Player player, int seconds) {
BossBar bar = bossBars.get(player.getUniqueId());
if (bar != null) {
bar.setTitle(ChatColor.GREEN + "Neulingsschutz: " + formatTime(seconds));
bar.setProgress(Math.max(0, seconds / (float) (durationMinutes * 60)));
}
}
// ---------- Datei Handling ---------- // ---------- Datei Handling ----------
private void loadData() { private void loadData() {
dataFile = new File(plugin.getDataFolder(), "newbieprotection.yml"); dataFile = new File(plugin.getDataFolder(), "newbieprotection.yml");
@@ -149,21 +197,47 @@ public class NewbieProtectionListener implements Listener {
} }
} }
dataConfig = YamlConfiguration.loadConfiguration(dataFile); dataConfig = YamlConfiguration.loadConfiguration(dataFile);
hasJoined.clear();
remainingSeconds.clear();
for (String key : dataConfig.getKeys(false)) { for (String key : dataConfig.getKeys(false)) {
try { try {
UUID uuid = UUID.fromString(key); UUID uuid = UUID.fromString(key);
int sec = dataConfig.getInt(key);
remainingSeconds.put(uuid, sec); // Gelesen: 'joined' Status und 'seconds'
boolean joined = dataConfig.getBoolean(key + ".joined", false);
int sec = dataConfig.getInt(key + ".seconds", 0);
if (joined) {
hasJoined.add(uuid);
}
if (sec > 0) {
remainingSeconds.put(uuid, sec);
}
} catch (IllegalArgumentException ignored) {} } catch (IllegalArgumentException ignored) {}
} }
} }
// WICHTIG: Public gemacht, damit von SurvivalPlus.java aufrufbar
public void saveData() { public void saveData() {
if (dataConfig == null) return; dataConfig = YamlConfiguration.loadConfiguration(dataFile);
for (Map.Entry<UUID, Integer> entry : remainingSeconds.entrySet()) {
dataConfig.set(entry.getKey().toString(), entry.getValue()); // Wir speichern ALLE Spieler, die schon mal gejoint sind (hasJoined)
// damit wir beim Rejoin wissen, dass sie keinen Neulingsschutz mehr bekommen.
for (UUID uuid : hasJoined) {
dataConfig.set(uuid + ".joined", true);
Integer sec = remainingSeconds.get(uuid);
if (sec != null && sec > 0) {
dataConfig.set(uuid + ".seconds", sec);
} else {
// Wenn Schutz abgelaufen (0 oder nicht in Map), speichern wir 0 oder löschen wir den Key?
// Besser: löschen wir den Key "seconds", aber "joined" bleibt true.
dataConfig.set(uuid + ".seconds", null);
}
} }
try { try {
dataConfig.save(dataFile); dataConfig.save(dataFile);
} catch (IOException e) { } catch (IOException e) {

View File

@@ -0,0 +1,13 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
/**
* Dieser Listener wird nicht mehr benötigt, da NickLoadListener
* mit ProtocolLib die Join/Quit Messages direkt auf Packet-Ebene behandelt.
*
* Diese Klasse kann gelöscht werden oder bleibt als leere Klasse bestehen.
*/
public class NickJoinMessageListener {
// Nicht mehr benötigt - ProtocolLib übernimmt die Arbeit
}

View File

@@ -2,10 +2,13 @@ package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.bukkit.event.Listener; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -19,26 +22,78 @@ public class NickLoadListener implements Listener {
this.plugin = plugin; this.plugin = plugin;
} }
@EventHandler @EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
String rawNick = plugin.getNicknamesConfig().getString(player.getUniqueId().toString());
// Immer die Standard-Join-Message unterdrücken — wir senden eine einzelne kontrollierte Nachricht später
event.setJoinMessage(null);
String rawNick = plugin.getNicknamesConfig().getString(player.getUniqueId().toString());
if (rawNick != null && !rawNick.isEmpty()) { if (rawNick != null && !rawNick.isEmpty()) {
String coloredNick = translateColors(rawNick); String coloredNick = translateColors(rawNick);
String finalNick = "§f[" + coloredNick + "§f]"; String plainNick = ChatColor.stripColor(coloredNick);
player.setDisplayName(finalNick);
player.setPlayerListName(finalNick); // Kurz: DisplayName jetzt auf echten Namen lassen, damit andere Plugins / Bukkit intern konsistent sind.
player.setDisplayName(player.getName());
// In 1 Tick: DisplayName für Chat setzen UND die Join-Nachricht EINMAL manuell versenden
Bukkit.getScheduler().runTask(plugin, () -> {
// Chat-Display: [Nick]
player.setDisplayName("§f[" + coloredNick + "§f]");
// Nachricht an alle Spieler senden (keine Doppelung, weil wir event.setJoinMessage(null) gesetzt haben)
String message = coloredNick + " §ejoined the game";
for (Player p : Bukkit.getOnlinePlayers()) {
p.sendMessage(message);
}
// Log in Konsole (sauber, ohne Farb-Codes)
plugin.getLogger().info(plainNick + " joined the game");
});
} else {
// Kein Nick vorhanden -> benutze Standardverhalten, aber ebenfalls manuell, um Doppelungen zu verhindern
player.setDisplayName(player.getName());
Bukkit.getScheduler().runTask(plugin, () -> {
String message = player.getName() + " §ejoined the game";
for (Player p : Bukkit.getOnlinePlayers()) {
p.sendMessage(message);
}
plugin.getLogger().info(player.getName() + " joined the game");
});
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
// Standard-Quit-Message unterdrücken
event.setQuitMessage(null);
String rawNick = plugin.getNicknamesConfig().getString(player.getUniqueId().toString());
if (rawNick != null && !rawNick.isEmpty()) {
String coloredNick = translateColors(rawNick);
String plainNick = ChatColor.stripColor(coloredNick);
// Sende Quit-Nachricht einmal manuell
String msg = coloredNick + " §eleft the game";
for (Player p : Bukkit.getOnlinePlayers()) {
p.sendMessage(msg);
}
plugin.getLogger().info(plainNick + " left the game");
} else {
String msg = player.getName() + " §eleft the game";
for (Player p : Bukkit.getOnlinePlayers()) {
p.sendMessage(msg);
}
plugin.getLogger().info(player.getName() + " left the game");
} }
} }
private String translateColors(String input) { private String translateColors(String input) {
String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input); String withLegacy = org.bukkit.ChatColor.translateAlternateColorCodes('&', input);
return replaceHexColors(withLegacy); Matcher matcher = HEX_PATTERN.matcher(withLegacy);
}
private String replaceHexColors(String input) {
Matcher matcher = HEX_PATTERN.matcher(input);
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
while (matcher.find()) { while (matcher.find()) {
String hexCode = matcher.group(); String hexCode = matcher.group();

View File

@@ -0,0 +1,324 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
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.SignChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.RegisteredServiceProvider;
public class SignShopListener implements Listener {
private final SurvivalPlus plugin;
private Economy economy;
public SignShopListener(SurvivalPlus plugin) {
this.plugin = plugin;
try {
if (plugin.getServer().getPluginManager().isPluginEnabled("Vault")) {
RegisteredServiceProvider<Economy> rsp = plugin.getServer().getServicesManager().getRegistration(Economy.class);
if (rsp != null) {
this.economy = rsp.getProvider();
plugin.getLogger().info("Vault Economy für Sign Shops gefunden.");
} else {
plugin.getLogger().warning("Vault Economy Service nicht gefunden!");
}
} else {
plugin.getLogger().warning("Vault Plugin nicht installiert! Sign Shops funktionieren nicht.");
}
} catch (Exception e) {
plugin.getLogger().warning("Fehler beim Laden der Vault API.");
}
}
// --- Schild erstellung ---
@EventHandler(priority = EventPriority.HIGHEST)
public void onSignChange(SignChangeEvent event) {
String line0Raw = event.getLine(0);
if (!line0Raw.equalsIgnoreCase("[buy]") && !line0Raw.equalsIgnoreCase("[sell]")) {
return;
}
Player player = event.getPlayer();
if (!player.hasPermission("survivalplus.shop.create")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu erstellen.");
return;
}
String line2 = event.getLine(1);
String line3 = event.getLine(2);
String[] parts = line2.split(" ");
if (parts.length < 2) {
player.sendMessage(ChatColor.RED + "Format in Zeile 2 falsch! Beispiel: 64 Diamond");
event.setCancelled(true);
return;
}
try {
Integer.parseInt(parts[0]);
if (Material.matchMaterial(parts[1]) == null) {
player.sendMessage(ChatColor.RED + "Das Item '" + parts[1] + "' existiert nicht.");
event.setCancelled(true);
return;
}
Double.parseDouble(line3);
} catch (Exception e) {
player.sendMessage(ChatColor.RED + "Fehler im Format! Benutze: [Buy] \n 64 Diamond \n 500");
event.setCancelled(true);
return;
}
// --- NEU: Schild farbig machen ---
if (line0Raw.equalsIgnoreCase("[buy]")) {
event.setLine(0, ChatColor.GREEN + "[BUY]");
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
} else if (line0Raw.equalsIgnoreCase("[sell]")) {
event.setLine(0, ChatColor.RED + "[SELL]");
event.setLine(1, ChatColor.WHITE + parts[0] + " " + ChatColor.AQUA + parts[1]);
event.setLine(2, ChatColor.GOLD + line3 + " Coins");
}
if (event.getLine(3) != null && !event.getLine(3).isEmpty()) {
event.setLine(3, ChatColor.GRAY + event.getLine(3));
}
}
// --- Interaktion ---
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerInteract(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null || !(block.getState() instanceof Sign)) {
return;
}
Sign sign = (Sign) block.getState();
String line0 = ChatColor.stripColor(sign.getLine(0));
if (!line0.equalsIgnoreCase("[buy]") && !line0.equalsIgnoreCase("[sell]")) {
return;
}
if (economy == null) {
event.getPlayer().sendMessage(ChatColor.RED + "Shop System offline (Vault fehlt).");
return;
}
// --- FEATURE: SHIFT+KLICK LOGIK (LÖSCHEN) ---
if (event.getPlayer().isSneaking()) {
Player player = event.getPlayer();
if (!player.hasPermission("survivalplus.shop.create")) {
player.sendMessage(ChatColor.RED + "Keine Berechtigung, Shops zu entfernen.");
event.setCancelled(true);
return;
}
block.breakNaturally();
player.sendMessage(ChatColor.GREEN + "Shop entfernt.");
event.setCancelled(true);
return;
}
// --- ROUTING ---
if (line0.equalsIgnoreCase("[buy]")) {
handleBuy(event, sign);
} else if (line0.equalsIgnoreCase("[sell]")) {
handleSell(event, sign);
}
}
// --- KAUFS LOGIK (ROBUST) ---
private void handleBuy(PlayerInteractEvent event, Sign sign) {
event.setCancelled(true); // Interaktion stoppen
Player player = event.getPlayer();
String line1Raw = ChatColor.stripColor(sign.getLine(1)); // "64 Diamond"
String line2Raw = ChatColor.stripColor(sign.getLine(2)); // "500" oder "500 Coins"
// 1. Validierung: Zeilen leer?
if (line1Raw == null || line1Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 1 des Schildes ist leer! (Format: 64 Diamond)");
return;
}
if (line2Raw == null || line2Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Format: 500)");
return;
}
// 2. Parsing: Zeile 1 "64 Diamond"
String[] partsRaw = line1Raw.split(" ");
if (partsRaw.length < 2) {
player.sendMessage(ChatColor.RED + "Falsches Format! Benutze: [Buy] \n 64 Diamond");
return;
}
String amountStr = partsRaw[0].trim(); // "64"
String itemNameStr = partsRaw[1].trim(); // "Diamond"
if (amountStr.isEmpty() || itemNameStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Format Fehler!");
return;
}
int amount;
Material material;
try {
amount = Integer.parseInt(amountStr);
material = Material.matchMaterial(itemNameStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Angebot '" + amountStr + "' ist keine Zahl! (Beispiel: 64)");
return;
}
if (material == null) {
player.sendMessage(ChatColor.RED + "Item '" + itemNameStr + "' existiert nicht!");
return;
}
// 3. Parsing: Zeile 2 "500 Coins" (Hier passierte der Fehler!)
String[] priceParts = line2Raw.split(" ");
if (priceParts.length == 0) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer! (Beispiel: 500)");
return;
}
String priceStr = priceParts[0].trim();
if (priceStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Kein Preis gefunden! (Beispiel: 500)");
return;
}
double price;
try {
price = Double.parseDouble(priceStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl! (Beispiel: 500)");
return;
} catch (ArrayIndexOutOfBoundsException e) {
player.sendMessage(ChatColor.RED + "Fehlerhafter Preis! (Beispiel: 500)");
return;
}
if (amount <= 0 || price < 0) {
player.sendMessage(ChatColor.RED + "Negative Werte sind nicht erlaubt.");
return;
}
// 4. Transaktion
try {
ItemStack item = new ItemStack(material, amount);
if (economy.getBalance(player) >= price) {
EconomyResponse response = economy.withdrawPlayer(player, price);
if (response.transactionSuccess()) {
player.getInventory().addItem(item);
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " gekauft!");
} else {
player.sendMessage(ChatColor.RED + "Fehler bei der Transaktion.");
}
} else {
player.sendMessage(ChatColor.RED + "Du hast nicht genug Geld! Du brauchst " + price + ".");
}
} catch (Exception e) {
player.sendMessage(ChatColor.RED + "Ein Fehler ist beim Lesen des Shops aufgetreten.");
// Loggen für Debugging, falls Vault spinnt
e.printStackTrace();
}
}
// --- VERKAUFS LOGIK ---
private void handleSell(PlayerInteractEvent event, Sign sign) {
event.setCancelled(true);
Player player = event.getPlayer();
String line1Raw = ChatColor.stripColor(sign.getLine(1));
String line2Raw = ChatColor.stripColor(sign.getLine(2));
if (line1Raw == null || line1Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 1 ist leer!");
return;
}
if (line2Raw == null || line2Raw.isEmpty()) {
player.sendMessage(ChatColor.RED + "Zeile 2 ist leer!");
return;
}
String[] partsRaw = line1Raw.split(" ");
if (partsRaw.length < 2) {
player.sendMessage(ChatColor.RED + "Format falsch! Benutze: [Sell] \n 64 Diamond");
return;
}
String amountStr = partsRaw[0].trim();
String itemNameStr = partsRaw[1].trim();
int amount;
Material material;
try {
amount = Integer.parseInt(amountStr);
material = Material.matchMaterial(itemNameStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Menge ist keine Zahl!");
return;
}
if (material == null) {
player.sendMessage(ChatColor.RED + "Item existiert nicht!");
return;
}
// Preis Parsing (wie im Kauf, nur sicherer)
String[] priceParts = line2Raw.split(" ");
if (priceParts.length == 0) {
player.sendMessage(ChatColor.RED + "Preiszeile ist leer!");
return;
}
String priceStr = priceParts[0].trim();
if (priceStr.isEmpty()) {
player.sendMessage(ChatColor.RED + "Kein Preis!");
return;
}
double price;
try {
price = Double.parseDouble(priceStr);
} catch (NumberFormatException e) {
player.sendMessage(ChatColor.RED + "Preis '" + priceStr + "' ist keine Zahl!");
return;
}
if (amount <= 0 || price < 0) {
player.sendMessage(ChatColor.RED + "Negativer Wert!");
return;
}
ItemStack tempItem = new ItemStack(material, amount);
int hasAmount = 0;
for (ItemStack item : player.getInventory().getStorageContents()) {
if (item != null && item.isSimilar(tempItem)) {
hasAmount += item.getAmount();
}
}
if (hasAmount < amount) {
player.sendMessage(ChatColor.RED + "Du hast nicht genug Items!");
return;
}
// Verkauf durchführen
player.getInventory().removeItem(new ItemStack(material, amount));
economy.depositPlayer(player, price);
player.updateInventory();
player.sendMessage(ChatColor.GREEN + "Du hast " + amount + " " + material.name().toLowerCase() + " für " + price + " Coins verkauft!");
}
}

View File

@@ -1,19 +1,23 @@
package de.viper.survivalplus.listeners; package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.ArmorStand; import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.event.block.Action;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@@ -22,9 +26,12 @@ import java.util.logging.Level;
public class SitListener implements Listener { public class SitListener implements Listener {
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private final Map<UUID, ArmorStand> sittingPlayers = new HashMap<>(); private final Map<UUID, ArmorStand> sittingPlayers = new HashMap<>();
private final Map<UUID, Long> sitCooldown = new HashMap<>(); // Neuer Cooldown gegen "Auto-Standup"
private static final String SIT_TAG = "SP_SIT_STAND";
public SitListener(SurvivalPlus plugin) { public SitListener(SurvivalPlus plugin) {
this.plugin = plugin; this.plugin = plugin;
cleanUpGhostStands();
} }
public boolean isSitting(Player player) { public boolean isSitting(Player player) {
@@ -42,22 +49,50 @@ public class SitListener implements Listener {
armorStand.setMarker(true); armorStand.setMarker(true);
armorStand.setInvisible(true); armorStand.setInvisible(true);
armorStand.setInvulnerable(true); armorStand.setInvulnerable(true);
armorStand.addScoreboardTag(SIT_TAG);
// Setze Rotation des Stands auf Rotation des Spielers
armorStand.setRotation(player.getLocation().getYaw(), player.getLocation().getPitch());
// Füge Spieler als Passenger hinzu
armorStand.addPassenger(player); armorStand.addPassenger(player);
// FIX: KEIN player.teleport() hier ausführen!
// In neueren Versionen "bricht" das Teleportieren das Mounten sofort wieder.
// Der Spieler wird automatisch durch addPassenger zum Stand teleportiert.
// Cooldown setzen, damit wir nicht sofort durch "Movement" aufstehen
sitCooldown.put(player.getUniqueId(), System.currentTimeMillis() + 500L);
sittingPlayers.put(player.getUniqueId(), armorStand); sittingPlayers.put(player.getUniqueId(), armorStand);
FileConfiguration lang = plugin.getLangConfig(); FileConfiguration lang = plugin.getLangConfig();
player.sendMessage(lang.getString("sit.success", "§aDu hast dich hingesetzt!")); player.sendMessage(lang.getString("sit.success", "§aDu hast dich hingesetzt!"));
plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " sitzt bei " + locationToString(location));
} }
public void standUp(Player player) { public void standUp(Player player) {
UUID playerId = player.getUniqueId(); UUID playerId = player.getUniqueId();
ArmorStand armorStand = sittingPlayers.remove(playerId); ArmorStand armorStand = sittingPlayers.remove(playerId);
sitCooldown.remove(playerId); // Cooldown löschen
if (armorStand != null) { if (armorStand != null) {
armorStand.remove(); if (player.isValid() && armorStand.isValid()) {
// Wenn man absteigt, landet man oft leicht daneben. Wir korrigieren das leicht,
// aber nicht so aggressiv wie beim Hinsetzen.
Location loc = player.getLocation().add(0, 0.2, 0);
armorStand.removePassenger(player);
armorStand.remove();
// Kleiner Fix, damit man nicht im Boden klebt
player.teleport(loc);
} else {
armorStand.remove();
}
FileConfiguration lang = plugin.getLangConfig(); FileConfiguration lang = plugin.getLangConfig();
player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!")); if (player.isOnline()) {
plugin.getLogger().log(Level.FINE, "Spieler " + player.getName() + " ist aufgestanden"); player.sendMessage(lang.getString("sit.stand-up", "§aDu bist aufgestanden!"));
}
} }
} }
@@ -71,7 +106,6 @@ public class SitListener implements Listener {
FileConfiguration lang = plugin.getLangConfig(); FileConfiguration lang = plugin.getLangConfig();
if (!player.hasPermission("survivalplus.sit")) { if (!player.hasPermission("survivalplus.sit")) {
player.sendMessage(lang.getString("no-permission", "§cDu hast keine Berechtigung für diesen Befehl!"));
return; return;
} }
@@ -80,35 +114,64 @@ public class SitListener implements Listener {
return; return;
} }
// Wenn der Spieler bereits sitzt, stehe auf double distance = player.getLocation().distance(block.getLocation());
if (distance > 3.0) {
return;
}
if (sittingPlayers.containsKey(player.getUniqueId())) { if (sittingPlayers.containsKey(player.getUniqueId())) {
standUp(player); standUp(player);
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
// Setze den Spieler genau auf der Treppenstufe if (player.isInsideVehicle()) return;
// Position berechnen
Location location = block.getLocation(); Location location = block.getLocation();
location.setX(location.getX() + 0.5); location.setX(location.getX() + 0.5);
location.setY(location.getY() + 0.5); // Genau auf der Treppenstufe (halbe Blockhöhe)
location.setZ(location.getZ() + 0.5); location.setZ(location.getZ() + 0.5);
// Bei Treppen: Y + 0.5 ist meistens korrekt für die Sitzfläche
location.setY(location.getY() + 0.5);
// Drehung anpassen, damit man nicht auf der Treppe schräg sitzt
location.setYaw(player.getLocation().getYaw());
location.setPitch(player.getLocation().getPitch());
sitPlayer(player, location); sitPlayer(player, location);
event.setCancelled(true); // Verhindere andere Interaktionen mit der Treppe event.setCancelled(true);
} }
@EventHandler @EventHandler
public void onPlayerMove(PlayerMoveEvent event) { public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID playerId = player.getUniqueId(); UUID playerId = player.getUniqueId();
if (!sittingPlayers.containsKey(playerId)) {
return;
}
// Prüfe, ob der Spieler sich bewegt hat (nur Positionsänderung, nicht Kopfbewegung) // Wenn der Spieler sitzt
Location from = event.getFrom(); if (sittingPlayers.containsKey(playerId)) {
Location to = event.getTo();
if (from.getX() != to.getX() || from.getY() != to.getY() || from.getZ() != to.getZ()) { // FIX: Cooldown prüfen (Verhindert "Auto-Standup" durch Lag beim Hinsetzen)
standUp(player); Long cooldown = sitCooldown.get(playerId);
if (cooldown != null && cooldown > System.currentTimeMillis()) {
return;
}
Location from = event.getFrom();
Location to = event.getTo();
if (to == null) return;
// Prüfe signifikante Bewegung
if (from.distanceSquared(to) > 0.0025) {
standUp(player);
}
}
}
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
if (sittingPlayers.containsKey(event.getPlayer().getUniqueId())) {
standUp(event.getPlayer());
} }
} }
@@ -122,7 +185,18 @@ public class SitListener implements Listener {
return material.name().endsWith("_STAIRS"); return material.name().endsWith("_STAIRS");
} }
private String locationToString(Location loc) { private void cleanUpGhostStands() {
return String.format("x=%.2f, y=%.2f, z=%.2f, world=%s", loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName()); Bukkit.getScheduler().runTaskLater(plugin, () -> {
for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntities()) {
if (entity instanceof ArmorStand) {
if (entity.getScoreboardTags().contains(SIT_TAG)) {
entity.remove();
}
}
}
}
plugin.logDebug("Ghost Stands (Sit) bereinigt.");
}, 20L);
} }
} }

View File

@@ -56,19 +56,12 @@ public class ToolUpgradeListener implements Listener {
if (!(event.getInventory() instanceof CraftingInventory)) return; if (!(event.getInventory() instanceof CraftingInventory)) return;
CraftingInventory inv = (CraftingInventory) event.getInventory(); CraftingInventory inv = (CraftingInventory) event.getInventory();
ItemStack[] matrix = inv.getMatrix(); ItemStack[] matrix = inv.getMatrix();
if (matrix.length < 9) { if (matrix.length < 9) return; // Kein 3x3-Crafting-Tisch
inv.setResult(null);
return;
}
ItemStack center = matrix[4]; ItemStack center = matrix[4];
if (center == null || center.getType() == Material.AIR) { // Nur eingreifen, wenn ein Werkzeug in der Mitte liegt
inv.setResult(null); if (center == null || center.getType() == Material.AIR || !isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) {
return; return; // Standard-Rezepte nicht beeinflussen
}
if (!isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) {
inv.setResult(null);
return;
} }
int currentLevel = getToolLevel(center); int currentLevel = getToolLevel(center);
@@ -83,7 +76,7 @@ public class ToolUpgradeListener implements Listener {
return; return;
} }
// Prüfen ob alle Slots (außer Mitte) mind. 2 Stück vom richtigen Material haben // Prüfen, ob alle Slots (außer Mitte) mind. 2 Stück vom richtigen Material haben
for (int i = 0; i < matrix.length; i++) { for (int i = 0; i < matrix.length; i++) {
if (i == 4) continue; if (i == 4) continue;
if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) { if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) {
@@ -106,20 +99,33 @@ public class ToolUpgradeListener implements Listener {
ItemStack[] matrix = inv.getMatrix(); ItemStack[] matrix = inv.getMatrix();
ItemStack center = matrix[4]; ItemStack center = matrix[4];
if (center == null || !isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) return; if (center == null || !isUpgradeableTool(center.getType()) || isWoodTool(center.getType())) {
plugin.getLogger().fine("Kein Upgrade-Werkzeug in der Mitte: " + (center != null ? center.getType() : "null"));
return;
}
int currentLevel = getToolLevel(center); int currentLevel = getToolLevel(center);
if (currentLevel >= maxLevel || currentLevel >= getMaxAllowedLevel(center.getType())) return; if (currentLevel >= maxLevel || currentLevel >= getMaxAllowedLevel(center.getType())) {
plugin.getLogger().fine("Maximales Level erreicht für Werkzeug: " + center.getType());
return;
}
Material requiredMat = levelMaterials.get(currentLevel + 1); Material requiredMat = levelMaterials.get(currentLevel + 1);
if (requiredMat == null) return; if (requiredMat == null) {
plugin.getLogger().fine("Kein Material für Level " + (currentLevel + 1));
return;
}
for (int i = 0; i < matrix.length; i++) { for (int i = 0; i < matrix.length; i++) {
if (i == 4) continue; if (i == 4) continue;
if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) return; if (matrix[i] == null || matrix[i].getType() != requiredMat || matrix[i].getAmount() < REQUIRED_AMOUNT) {
plugin.getLogger().fine("Ungültiges Material in Slot " + i + ": " + (matrix[i] != null ? matrix[i].getType() : "null"));
return;
}
} }
// Gültig: Event abbrechen, Upgrade geben, Items verbrauchen // Gültig: Event abbrechen, Upgrade geben, Items verbrauchen
plugin.getLogger().info("Werkzeug-Upgrade für " + player.getName() + ": " + center.getType() + " auf Level " + (currentLevel + 1));
event.setCancelled(true); event.setCancelled(true);
ItemStack result = createUpgradedTool(center, currentLevel + 1); ItemStack result = createUpgradedTool(center, currentLevel + 1);
@@ -215,7 +221,9 @@ public class ToolUpgradeListener implements Listener {
n.endsWith("_SWORD"); n.endsWith("_SWORD");
} }
private boolean isWoodTool(Material mat) { return mat.name().startsWith("WOODEN"); } private boolean isWoodTool(Material mat) {
return mat.name().startsWith("WOODEN");
}
private int getMaxAllowedLevel(Material mat) { private int getMaxAllowedLevel(Material mat) {
String s = mat.name(); String s = mat.name();

View File

@@ -0,0 +1,88 @@
package de.viper.survivalplus.listeners;
import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent; // NEU: EntityPickupItemEvent
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
public class VanishListener implements Listener {
private final SurvivalPlus plugin;
public VanishListener(SurvivalPlus plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (plugin.isVanished(player.getUniqueId())) {
// Join Message ausblenden
event.setJoinMessage(null);
// Silent Join (Teleport-Effekt) via ProtocolLib
if (player.hasPermission("survivalplus.vanish.silent")) {
hideJoinParticles(player);
}
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
if (plugin.isVanished(player.getUniqueId())) {
// Quit Message ausblenden
event.setQuitMessage(null);
}
}
@EventHandler
public void onTeleport(PlayerTeleportEvent event) {
Player player = event.getPlayer();
// Silent Teleport verhindert den Endermann-Teleportations-Effekt
if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.silent")) {
// In 4.8.0 ist das stumme Teleportieren komplex.
// Wir lassen es hier beim Abschalten der Join-Partikel bewenden.
}
}
@EventHandler
public void onPickup(EntityPickupItemEvent event) {
if (!(event.getEntity() instanceof Player)) return; // Vorsichtshalber prüfen
Player player = (Player) event.getEntity();
if (plugin.isVanished(player.getUniqueId()) && player.hasPermission("survivalplus.vanish.no-pickup")) {
event.setCancelled(true); // Item nicht aufheben
}
}
private void hideJoinParticles(Player player) {
if (!plugin.getServer().getPluginManager().isPluginEnabled("ProtocolLib")) return;
try {
// Direkter Cast auf ProtocolManager ist in 4.8.0 sicherer
ProtocolManager manager = (ProtocolManager) plugin.getServer().getPluginManager().getPlugin("ProtocolLib");
if (manager == null) return;
// Packet Container erstellen
PacketContainer packet = manager.createPacket(PacketType.Play.Server.ENTITY_STATUS);
packet.getIntegers().write(0, player.getEntityId());
packet.getBytes().write(0, (byte) 0x20); // 32 = Invisibel/Unsichtbar Status Client-Side
for (Player online : plugin.getServer().getOnlinePlayers()) {
if (online.equals(player)) continue;
manager.sendServerPacket(online, packet);
}
} catch (Exception e) {
// Ignorieren Fehler bei ProtocolLib
}
}
}

View File

@@ -6,6 +6,7 @@ import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
@@ -16,23 +17,34 @@ public class BackpackRecipe {
ItemStack backpack = new ItemStack(Material.CHEST); ItemStack backpack = new ItemStack(Material.CHEST);
ItemMeta meta = backpack.getItemMeta(); ItemMeta meta = backpack.getItemMeta();
if (meta != null) { if (meta != null) {
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', langConfig.getString("backpack.name", "&eRucksack"))); // Fallback falls der Key in der lang.yml fehlt
String displayName = langConfig.getString("backpack.name", "&eRucksack");
meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', displayName));
backpack.setItemMeta(meta); backpack.setItemMeta(meta);
} }
NamespacedKey key = new NamespacedKey(plugin, "backpack"); NamespacedKey key = new NamespacedKey(plugin, "backpack");
// Rezept erstellen
ShapedRecipe recipe = new ShapedRecipe(key, backpack); ShapedRecipe recipe = new ShapedRecipe(key, backpack);
// Form festlegen
recipe.shape( recipe.shape(
"S L", // Faden, leer, Leder "S L",
" C ", // leer, Truhe, leer " C ",
"S L" // Faden, leer, Leder "S L"
); );
recipe.setIngredient('S', Material.STRING); // Faden
recipe.setIngredient('L', Material.LEATHER); // Leder // Zutaten festlegen (mit RecipeChoice für bessere Kompatibilität)
recipe.setIngredient('C', Material.CHEST); // Truhe recipe.setIngredient('S', new RecipeChoice.MaterialChoice(Material.STRING));
recipe.setIngredient('L', new RecipeChoice.MaterialChoice(Material.LEATHER));
recipe.setIngredient('C', new RecipeChoice.MaterialChoice(Material.CHEST));
// WICHTIG: Setzt eine eigene Gruppe.
// Verhindert Konflikte mit Vanilla-Rezepten im Rezeptbuch.
recipe.setGroup("survivalplus");
Bukkit.addRecipe(recipe); Bukkit.addRecipe(recipe);
plugin.getLogger().info("Backpack Rezept wurde registriert."); plugin.getLogger().info("Backpack Rezept wurde erfolgreich registriert (Gruppe: survivalplus).");
} }
} }

View File

@@ -73,13 +73,14 @@ public class TradeManager {
return; return;
} }
TradeSession session = new TradeSession(plugin, sender, receiver); // Neue Session starten
TradeSession session = new TradeSession(plugin, sender, receiver, this);
Bukkit.getPluginManager().registerEvents(session, plugin); Bukkit.getPluginManager().registerEvents(session, plugin);
activeTrades.put(sender.getUniqueId(), session); activeTrades.put(sender.getUniqueId(), session);
activeTrades.put(receiver.getUniqueId(), session); activeTrades.put(receiver.getUniqueId(), session);
session.openInventories(); session.openInventory();
sender.sendMessage(plugin.getLangConfig().getString("trade.started-sender", "§aTrade gestartet mit %player%").replace("%player%", receiver.getName())); sender.sendMessage(plugin.getLangConfig().getString("trade.started-sender", "§aTrade gestartet mit %player%").replace("%player%", receiver.getName()));
receiver.sendMessage(plugin.getLangConfig().getString("trade.started-receiver", "§a%player% hat dich zu einem Trade eingeladen.").replace("%player%", sender.getName())); receiver.sendMessage(plugin.getLangConfig().getString("trade.started-receiver", "§a%player% hat dich zu einem Trade eingeladen.").replace("%player%", sender.getName()));
@@ -91,7 +92,7 @@ public class TradeManager {
Player r = session.getReceiver(); Player r = session.getReceiver();
if (s != null) activeTrades.remove(s.getUniqueId()); if (s != null) activeTrades.remove(s.getUniqueId());
if (r != null) activeTrades.remove(r.getUniqueId()); if (r != null) activeTrades.remove(r.getUniqueId());
session.endSession(); session.closeInventory();
} }
public TradeSession getTrade(Player player) { public TradeSession getTrade(Player player) {

View File

@@ -16,150 +16,297 @@ import org.bukkit.inventory.meta.ItemMeta;
public class TradeSession implements Listener { public class TradeSession implements Listener {
private final SurvivalPlus plugin; private final SurvivalPlus plugin;
private final TradeManager tradeManager;
private final Player sender; private final Player sender;
private final Player receiver; private final Player receiver;
private final Inventory invSender; // Wir benutzen nur EIN Inventar für beide (Size 54 = Double Chest)
private final Inventory invReceiver; // Slots 0-17: Angebot von Sender (Editierbar für Sender)
// Slots 18-35: Angebot von Receiver (Editierbar für Receiver)
// Slots 36-44: Trennung / Status
// Slot 45: Abbrechen
// Slot 53: Bestätigen
private final Inventory tradeInv;
private boolean senderConfirmed = false; private boolean senderConfirmed = false;
private boolean receiverConfirmed = false; private boolean receiverConfirmed = false;
private boolean ended = false; // Flag gegen Rekursion private boolean active = true;
public TradeSession(SurvivalPlus plugin, Player sender, Player receiver) { public TradeSession(SurvivalPlus plugin, Player sender, Player receiver, TradeManager tradeManager) {
this.plugin = plugin; this.plugin = plugin;
this.tradeManager = tradeManager;
this.sender = sender; this.sender = sender;
this.receiver = receiver; this.receiver = receiver;
String titleForSender = plugin.getLangConfig().getString("trade.inventory-title", "Handel mit %player%") String title = plugin.getLangConfig().getString("trade.inventory-title", "Handel")
.replace("%player%", receiver.getName()); .replace("%player%", receiver.getName())
String titleForReceiver = plugin.getLangConfig().getString("trade.inventory-title", "Handel mit %player%") .replace("%other%", sender.getName());
.replace("%player%", sender.getName());
this.invSender = Bukkit.createInventory(sender, 27, titleForSender); // Wir nutzen null als Owner, damit es ein gemeinsames Inventar ist
this.invReceiver = Bukkit.createInventory(receiver, 27, titleForReceiver); this.tradeInv = Bukkit.createInventory(null, 54, title);
setupLayout();
addConfirmButton(invSender);
addConfirmButton(invReceiver);
} }
private void addConfirmButton(Inventory inv) { private void setupLayout() {
ItemStack confirm = new ItemStack(Material.LIME_CONCRETE); // Trennung zwischen den beiden Angeboten (Glas)
ItemMeta meta = confirm.getItemMeta(); ItemStack glass = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
meta.setDisplayName(plugin.getLangConfig().getString("trade.confirm-button", "§aBestätigen")); ItemMeta glassMeta = glass.getItemMeta();
confirm.setItemMeta(meta); glassMeta.setDisplayName(" ");
inv.setItem(26, confirm); glass.setItemMeta(glassMeta);
for(int i = 36; i < 45; i++) {
tradeInv.setItem(i, glass);
}
// Buttons initialisieren
updateStatusItem();
updateButtons();
}
private void updateButtons() {
// Abbrechen Button (Rot)
ItemStack cancel = new ItemStack(Material.RED_CONCRETE);
ItemMeta cancelMeta = cancel.getItemMeta();
cancelMeta.setDisplayName(plugin.getLangConfig().getString("trade.cancel-button", "§cAbbrechen"));
cancel.setItemMeta(cancelMeta);
tradeInv.setItem(45, cancel);
// Bestätigen Button
Material confirmMat = Material.LIME_CONCRETE;
String confirmName = "§aBestätigen";
// Wenn beide bestätigt haben, ändert sich der Button
if (senderConfirmed && receiverConfirmed) {
confirmMat = Material.GOLD_BLOCK; // Signalisiert Tauschvorgang
confirmName = "§6Handel läuft...";
} else if (senderConfirmed) {
confirmMat = Material.YELLOW_CONCRETE;
confirmName = "§eWarte auf Partner...";
} else if (receiverConfirmed) {
confirmMat = Material.YELLOW_CONCRETE;
confirmName = "§eWarte auf Partner...";
}
ItemStack confirm = new ItemStack(confirmMat);
ItemMeta confirmMeta = confirm.getItemMeta();
confirmMeta.setDisplayName(confirmName);
confirm.setItemMeta(confirmMeta);
tradeInv.setItem(53, confirm);
}
private void updateStatusItem() {
// Slot 40 zeigt den Status an
String statusText = "§eWarte auf Bestätigung...";
if (senderConfirmed && !receiverConfirmed) {
statusText = "§a" + sender.getName() + " §7hat bestätigt.";
} else if (!senderConfirmed && receiverConfirmed) {
statusText = "§a" + receiver.getName() + " §7hat bestätigt.";
} else if (senderConfirmed && receiverConfirmed) {
statusText = "§6Handel wird ausgeführt...";
}
ItemStack status = new ItemStack(Material.BOOK);
ItemMeta statusMeta = status.getItemMeta();
statusMeta.setDisplayName(statusText);
status.setItemMeta(statusMeta);
tradeInv.setItem(40, status);
} }
public Player getSender() { return sender; } public Player getSender() { return sender; }
public Player getReceiver() { return receiver; } public Player getReceiver() { return receiver; }
public void openInventories() { public void openInventory() {
if (sender.isOnline()) sender.openInventory(invSender); if (sender.isOnline()) sender.openInventory(tradeInv);
if (receiver.isOnline()) receiver.openInventory(invReceiver); if (receiver.isOnline()) receiver.openInventory(tradeInv);
} }
private void returnItems(Player player, Inventory inventory) { public void closeInventory() {
for (int i = 0; i < 26; i++) { active = false;
ItemStack item = inventory.getItem(i); if (sender.isOnline() && sender.getOpenInventory().getTopInventory() == tradeInv) {
if (item != null) player.getInventory().addItem(item);
}
}
public void endSession() {
if (ended) return; // Rekursion verhindern
ended = true;
// Items zurückgeben
if (sender.isOnline()) returnItems(sender, invSender);
if (receiver.isOnline()) returnItems(receiver, invReceiver);
// Inventories schließen
if (sender.isOnline() && sender.getOpenInventory().getTopInventory() == invSender) {
sender.closeInventory(); sender.closeInventory();
} }
if (receiver.isOnline() && receiver.getOpenInventory().getTopInventory() == invReceiver) { if (receiver.isOnline() && receiver.getOpenInventory().getTopInventory() == tradeInv) {
receiver.closeInventory(); receiver.closeInventory();
} }
HandlerList.unregisterAll(this); HandlerList.unregisterAll(this);
} }
private Inventory getOwnInventory(Player p) { private void returnItems() {
return p.getUniqueId().equals(sender.getUniqueId()) ? invSender : invReceiver; // Gibt alle Items zurück, falls der Handel abgebrochen wurde
} for (int i = 0; i < 36; i++) {
ItemStack item = tradeInv.getItem(i);
private Inventory getOtherInventory(Player p) { if (item != null) {
return p.getUniqueId().equals(sender.getUniqueId()) ? invReceiver : invSender; // Bestimmen, wem das Item gehört
} if (i < 18) {
if (sender.isOnline()) sender.getInventory().addItem(item);
private void updateOtherView(Player p) { } else {
Inventory own = getOwnInventory(p); if (receiver.isOnline()) receiver.getInventory().addItem(item);
Inventory other = getOtherInventory(p); }
for (int i = 0; i < 26; i++) { tradeInv.setItem(i, null); // Slot leeren
other.setItem(i, own.getItem(i)); }
} }
} }
private void executeTrade() { private void executeTrade() {
for (int i = 0; i < 26; i++) { // Tauschvorgang
ItemStack itemFromSender = invSender.getItem(i); // 1. Sender Items zu Receiver
ItemStack itemFromReceiver = invReceiver.getItem(i); for (int i = 0; i < 18; i++) {
ItemStack item = tradeInv.getItem(i);
if (item != null) {
if (receiver.isOnline()) receiver.getInventory().addItem(item);
tradeInv.setItem(i, null);
}
}
if (itemFromSender != null) sender.getInventory().addItem(itemFromSender); // 2. Receiver Items zu Sender
if (itemFromReceiver != null) receiver.getInventory().addItem(itemFromReceiver); for (int i = 18; i < 36; i++) {
ItemStack item = tradeInv.getItem(i);
if (item != null) {
if (sender.isOnline()) sender.getInventory().addItem(item);
tradeInv.setItem(i, null);
}
} }
String success = plugin.getLangConfig().getString("trade.success", "§aHandel erfolgreich abgeschlossen!"); String success = plugin.getLangConfig().getString("trade.success", "§aHandel erfolgreich abgeschlossen!");
if (sender.isOnline()) sender.sendMessage(success); if (sender.isOnline()) sender.sendMessage(success);
if (receiver.isOnline()) receiver.sendMessage(success); if (receiver.isOnline()) receiver.sendMessage(success);
endSession(); closeInventory();
}
private void resetConfirmations() {
senderConfirmed = false;
receiverConfirmed = false;
} }
@EventHandler @EventHandler
public void onInventoryClick(InventoryClickEvent e) { public void onInventoryClick(InventoryClickEvent e) {
if (!active) return;
if (!(e.getWhoClicked() instanceof Player p)) return; if (!(e.getWhoClicked() instanceof Player p)) return;
if (!p.getUniqueId().equals(sender.getUniqueId()) && !p.getUniqueId().equals(receiver.getUniqueId())) return; if (e.getClickedInventory() == null) return;
Inventory top = e.getView().getTopInventory(); // Nur Klicks im Trade-Inventar behandeln
Inventory clicked = e.getClickedInventory(); if (e.getClickedInventory().getSize() != 54 || !e.getClickedInventory().equals(tradeInv)) return;
// Auch Klicks im Bottom Inventory erlauben, aber nicht logisch behandeln (Bukkit regelt das)
if (p.getUniqueId().equals(sender.getUniqueId())) {
handleSenderClick(e);
} else if (p.getUniqueId().equals(receiver.getUniqueId())) {
handleReceiverClick(e);
}
}
private void handleSenderClick(InventoryClickEvent e) {
int slot = e.getSlot(); int slot = e.getSlot();
if (clicked == null) return; // Abbrechen
if (slot == 45) {
// Bestätigen-Button
if (slot == 26 && clicked.equals(top)) {
e.setCancelled(true); e.setCancelled(true);
if (p.getUniqueId().equals(sender.getUniqueId())) senderConfirmed = true; sender.sendMessage(plugin.getLangConfig().getString("trade.cancelled", "§cHandel abgebrochen."));
if (p.getUniqueId().equals(receiver.getUniqueId())) receiverConfirmed = true; if (receiver.isOnline()) receiver.sendMessage(plugin.getLangConfig().getString("trade.cancelled-partner", "§cHandel wurde abgebrochen."));
tradeManager.endTrade(this);
p.sendMessage(plugin.getLangConfig().getString("trade.confirmed", "§aDu hast den Handel bestätigt!"));
if (senderConfirmed && receiverConfirmed) executeTrade();
return; return;
} }
// Nur das eigene Trade-Inventar editierbar // Bestätigen
if (clicked.equals(top)) { if (slot == 53) {
Bukkit.getScheduler().runTaskLater(plugin, () -> { e.setCancelled(true);
updateOtherView(p); if (receiverConfirmed) {
resetConfirmations(); executeTrade();
}, 1L); } else {
senderConfirmed = !senderConfirmed; // Toggle
sender.sendMessage(senderConfirmed ?
plugin.getLangConfig().getString("trade.confirmed", "§aBestätigt!") :
"§cBestätigung zurückgezogen.");
updateButtons();
updateStatusItem();
}
return;
} }
// Item-Bewegungen nur im eigenen Bereich (0-17) erlauben
if (slot >= 0 && slot <= 17) {
// Erlaubt
// Wenn ein Item bewegt wird, Bestätigungen zurücksetzen
if (e.getCurrentItem() != null || e.getCursor() != null) {
if (senderConfirmed || receiverConfirmed) {
senderConfirmed = false;
receiverConfirmed = false;
updateButtons();
updateStatusItem();
}
}
return;
}
// Alle anderen Slots sperren (Partner-Seite, Glas, Status)
e.setCancelled(true);
}
private void handleReceiverClick(InventoryClickEvent e) {
int slot = e.getSlot();
// Abbrechen
if (slot == 45) {
e.setCancelled(true);
receiver.sendMessage(plugin.getLangConfig().getString("trade.cancelled", "§cHandel abgebrochen."));
if (sender.isOnline()) sender.sendMessage(plugin.getLangConfig().getString("trade.cancelled-partner", "§cHandel wurde abgebrochen."));
tradeManager.endTrade(this);
return;
}
// Bestätigen
if (slot == 53) {
e.setCancelled(true);
if (senderConfirmed) {
executeTrade();
} else {
receiverConfirmed = !receiverConfirmed; // Toggle
receiver.sendMessage(receiverConfirmed ?
plugin.getLangConfig().getString("trade.confirmed", "§aBestätigt!") :
"§cBestätigung zurückgezogen.");
updateButtons();
updateStatusItem();
}
return;
}
// Item-Bewegungen nur im eigenen Bereich (18-35) erlauben
if (slot >= 18 && slot <= 35) {
// Erlaubt
if (e.getCurrentItem() != null || e.getCursor() != null) {
if (senderConfirmed || receiverConfirmed) {
senderConfirmed = false;
receiverConfirmed = false;
updateButtons();
updateStatusItem();
}
}
return;
}
// Alle anderen Slots sperren
e.setCancelled(true);
} }
@EventHandler @EventHandler
public void onInventoryClose(InventoryCloseEvent e) { public void onInventoryClose(InventoryCloseEvent e) {
if (!active) return;
if (!(e.getPlayer() instanceof Player p)) return; if (!(e.getPlayer() instanceof Player p)) return;
if (p.getUniqueId().equals(sender.getUniqueId()) || p.getUniqueId().equals(receiver.getUniqueId())) {
// Kleine Verzögerung, um StackOverflow zu vermeiden // Wenn ein Spieler das Inventar schließt, wird der Handel abgebrochen
Bukkit.getScheduler().runTaskLater(plugin, this::endSession, 1L); // Wir prüfen, ob noch Items drin sind, um Duplikate zu vermeiden
if (e.getInventory().equals(tradeInv)) {
if (p.getUniqueId().equals(sender.getUniqueId()) || p.getUniqueId().equals(receiver.getUniqueId())) {
// Verzögern, falls der Server den CloseEvent für beide fast gleichzeitig feuert
Bukkit.getScheduler().runTaskLater(plugin, () -> {
if (active) { // Nur wenn nicht schon durch executeTrade geschlossen
returnItems();
if (p.getUniqueId().equals(sender.getUniqueId())) {
if (receiver.isOnline()) receiver.sendMessage("§cDer Partner hat den Handel abgebrochen.");
} else {
if (sender.isOnline()) sender.sendMessage("§cDer Partner hat den Handel abgebrochen.");
}
tradeManager.endTrade(this);
}
}, 1L);
}
} }
} }
} }

View File

@@ -0,0 +1,28 @@
package de.viper.survivalplus.util;
import de.viper.survivalplus.SurvivalPlus;
import java.util.Arrays;
import java.util.List;
public class BannerManager {
private final SurvivalPlus plugin;
public BannerManager(SurvivalPlus plugin) {
this.plugin = plugin;
}
public void displayConsoleBanner() {
String version = plugin.getDescription().getVersion();
List<String> banner = Arrays.asList(
"******************************",
"* SurvivalPlus " + version + " *",
"* *",
"* M_Viper *",
"******************************"
);
for (String line : banner) {
plugin.getLogger().info(line);
}
}
}

View File

@@ -0,0 +1,91 @@
package de.viper.survivalplus.util;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class Claim {
private UUID owner;
private Set<UUID> trusted = new HashSet<>();
private int x1, z1, x2, z2;
private String worldName;
public Claim(Player owner, String worldName, int x1, int z1, int x2, int z2) {
this.owner = owner.getUniqueId();
this.worldName = worldName;
this.x1 = Math.min(x1, x2);
this.z1 = Math.min(z1, z2);
this.x2 = Math.max(x1, x2);
this.z2 = Math.max(z1, z2);
trusted.add(owner.getUniqueId());
}
public Claim(UUID owner, String worldName, int x1, int z1, int x2, int z2) {
this.owner = owner;
this.worldName = worldName;
this.x1 = Math.min(x1, x2);
this.z1 = Math.min(z1, z2);
this.x2 = Math.max(x1, x2);
this.z2 = Math.max(z1, z2);
trusted.add(owner);
}
public boolean canInteract(UUID uuid) {
return trusted.contains(uuid);
}
public void addTrusted(UUID uuid) {
trusted.add(uuid);
}
public void removeTrusted(UUID uuid) {
trusted.remove(uuid);
}
public UUID getOwner() {
return owner;
}
public Set<UUID> getTrusted() {
return trusted;
}
public int getX1() {
return x1;
}
public int getZ1() {
return z1;
}
public int getX2() {
return x2;
}
public int getZ2() {
return z2;
}
public String getWorldName() {
return worldName;
}
public boolean isInside(Location location) {
if (!location.getWorld().getName().equals(worldName)) {
return false;
}
int x = location.getBlockX();
int z = location.getBlockZ();
return x >= x1 && x <= x2 && z >= z1 && z <= z2;
}
public boolean overlaps(Claim other) {
if (!this.worldName.equals(other.worldName)) {
return false;
}
return !(this.x2 < other.x1 || this.x1 > other.x2 || this.z2 < other.z1 || this.z1 > other.z2);
}
}

View File

@@ -2,7 +2,10 @@ package de.viper.survivalplus.util;
import de.viper.survivalplus.SurvivalPlus; import de.viper.survivalplus.SurvivalPlus;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.type.Chest;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -10,9 +13,11 @@ import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@@ -27,6 +32,7 @@ public class LockSystem implements Listener, CommandExecutor {
private final Map<Location, LockData> lockedBlocks = new HashMap<>(); private final Map<Location, LockData> lockedBlocks = new HashMap<>();
private final Set<UUID> lockMode = new HashSet<>(); private final Set<UUID> lockMode = new HashSet<>();
private final Map<UUID, BukkitRunnable> lockTimeoutTasks = new HashMap<>(); private final Map<UUID, BukkitRunnable> lockTimeoutTasks = new HashMap<>();
private final Map<UUID, Long> lastDenyMessage = new HashMap<>();
private final File lockFile; private final File lockFile;
private FileConfiguration lockConfig; private FileConfiguration lockConfig;
@@ -139,7 +145,16 @@ public class LockSystem implements Listener, CommandExecutor {
if (lockedBlocks.containsKey(loc)) { if (lockedBlocks.containsKey(loc)) {
player.sendMessage(plugin.getMessage("lock.already-locked")); player.sendMessage(plugin.getMessage("lock.already-locked"));
} else { } else {
// Lock den Block
lockedBlocks.put(loc, new LockData(uuid.toString())); lockedBlocks.put(loc, new LockData(uuid.toString()));
// Prüfe auf Doppeltruhe und locke beide Hälften
Block otherChest = getOtherChestHalf(block);
if (otherChest != null) {
Location otherLoc = otherChest.getLocation();
lockedBlocks.put(otherLoc, new LockData(uuid.toString()));
}
saveLocks(); saveLocks();
player.sendMessage(plugin.getMessage("lock.locked")); player.sendMessage(plugin.getMessage("lock.locked"));
} }
@@ -149,7 +164,19 @@ public class LockSystem implements Listener, CommandExecutor {
} }
if (!isLockable) return; if (!isLockable) return;
if (!lockedBlocks.containsKey(loc)) return;
// Prüfe sowohl den geklickten Block als auch die andere Hälfte bei Doppeltruhen
if (!lockedBlocks.containsKey(loc)) {
Block otherChest = getOtherChestHalf(block);
if (otherChest != null) {
loc = otherChest.getLocation();
if (!lockedBlocks.containsKey(loc)) {
return;
}
} else {
return;
}
}
LockData lock = lockedBlocks.get(loc); LockData lock = lockedBlocks.get(loc);
String playerUUID = uuid.toString(); String playerUUID = uuid.toString();
@@ -162,6 +189,176 @@ public class LockSystem implements Listener, CommandExecutor {
event.setCancelled(true); event.setCancelled(true);
} }
// NEU: Verhindert das Öffnen von gelockten Türen durch Redstone (Druckplatten, Knöpfe, etc.)
@EventHandler(priority = EventPriority.HIGHEST)
public void onRedstoneChange(BlockRedstoneEvent event) {
Block block = event.getBlock();
// Prüfe, ob der Block eine Tür ist
if (!block.getType().name().contains("DOOR")) return;
Location loc = block.getLocation();
LockData lock = null;
// Prüfe, ob die Tür gelockt ist
if (lockedBlocks.containsKey(loc)) {
lock = lockedBlocks.get(loc);
} else {
// Prüfe auch die andere Hälfte der Tür (oben/unten)
Location above = loc.clone().add(0, 1, 0);
Location below = loc.clone().add(0, -1, 0);
if (lockedBlocks.containsKey(above)) {
lock = lockedBlocks.get(above);
} else if (lockedBlocks.containsKey(below)) {
lock = lockedBlocks.get(below);
}
}
// Wenn keine Lock gefunden wurde, erlaube die Änderung
if (lock == null) return;
// Wenn die Tür gelockt ist und Redstone-Signal empfängt, blockiere die Änderung
// Es sei denn, das Signal kommt von einem berechtigten Spieler (wird über Druckplatten-Event geprüft)
if (event.getNewCurrent() > 0) {
event.setNewCurrent(0);
}
}
// ZUSÄTZLICH: Verhindert Interaktion mit Druckplatten/Knöpfen in der Nähe von gelockten Türen
@EventHandler(priority = EventPriority.HIGHEST)
public void onPressurePlateInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.PHYSICAL) return;
Block block = event.getClickedBlock();
if (block == null) return;
// Prüfe, ob es eine Druckplatte ist
String typeName = block.getType().name();
if (!typeName.contains("PRESSURE_PLATE")) return;
Player player = event.getPlayer();
UUID playerUUID = player.getUniqueId();
// Prüfe alle angrenzenden Blöcke auf gelockte Türen
for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) {
Block relative = block.getRelative(face);
// Prüfe bis zu 2 Blöcke in jede Richtung
for (int i = 0; i < 3; i++) {
if (relative.getType().name().contains("DOOR")) {
Location doorLoc = relative.getLocation();
// Prüfe beide Türhälften
LockData lock = getLockData(doorLoc);
if (lock != null && !hasAccess(player, lock)) {
event.setCancelled(true);
// Anti-Spam: Nur alle 3 Sekunden eine Nachricht senden
long currentTime = System.currentTimeMillis();
Long lastMessage = lastDenyMessage.get(playerUUID);
if (lastMessage == null || (currentTime - lastMessage) > 3000) {
player.sendMessage(plugin.getMessage("lock.block-denied"));
lastDenyMessage.put(playerUUID, currentTime);
}
return;
}
}
relative = relative.getRelative(face);
}
}
}
private boolean isLockedDoor(Location loc) {
return lockedBlocks.containsKey(loc);
}
private LockData getLockData(Location loc) {
LockData lock = lockedBlocks.get(loc);
if (lock == null) {
lock = lockedBlocks.get(loc.clone().add(0, 1, 0));
}
if (lock == null) {
lock = lockedBlocks.get(loc.clone().add(0, -1, 0));
}
return lock;
}
private boolean hasAccess(Player player, LockData lock) {
String playerUUID = player.getUniqueId().toString();
return lock.getOwnerUUID().equals(playerUUID) ||
lock.isFriend(playerUUID) ||
player.isOp();
}
// Hilfsmethode: Findet die andere Hälfte einer Doppeltruhe
private Block getOtherChestHalf(Block block) {
if (block.getType() != Material.CHEST && block.getType() != Material.TRAPPED_CHEST) {
return null;
}
try {
// Versuche moderne BlockData API (1.13+)
if (block.getBlockData() instanceof Chest) {
Chest chestData = (Chest) block.getBlockData();
// Prüfe ob es eine Doppeltruhe ist
if (chestData.getType() == Chest.Type.SINGLE) {
return null;
}
// Finde die Richtung zur anderen Hälfte
BlockFace facing = chestData.getFacing();
BlockFace direction;
if (chestData.getType() == Chest.Type.LEFT) {
// Linke Hälfte: Andere Hälfte ist rechts
direction = getRight(facing);
} else {
// Rechte Hälfte: Andere Hälfte ist links
direction = getLeft(facing);
}
Block otherBlock = block.getRelative(direction);
if (otherBlock.getType() == block.getType()) {
return otherBlock;
}
}
} catch (Exception e) {
// Fallback für ältere Versionen: Prüfe alle angrenzenden Blöcke
for (BlockFace face : new BlockFace[]{BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST}) {
Block relative = block.getRelative(face);
if (relative.getType() == block.getType()) {
return relative;
}
}
}
return null;
}
private BlockFace getRight(BlockFace face) {
switch (face) {
case NORTH: return BlockFace.EAST;
case EAST: return BlockFace.SOUTH;
case SOUTH: return BlockFace.WEST;
case WEST: return BlockFace.NORTH;
default: return face;
}
}
private BlockFace getLeft(BlockFace face) {
switch (face) {
case NORTH: return BlockFace.WEST;
case WEST: return BlockFace.SOUTH;
case SOUTH: return BlockFace.EAST;
case EAST: return BlockFace.NORTH;
default: return face;
}
}
@EventHandler @EventHandler
public void onBlockBreak(BlockBreakEvent event) { public void onBlockBreak(BlockBreakEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
@@ -174,6 +371,13 @@ public class LockSystem implements Listener, CommandExecutor {
String playerUUID = player.getUniqueId().toString(); String playerUUID = player.getUniqueId().toString();
if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) { if (lock.getOwnerUUID().equals(playerUUID) || lock.isFriend(playerUUID) || player.isOp()) {
// Erlaube das Brechen und entferne beide Locks bei Doppeltruhen
lockedBlocks.remove(loc);
Block otherChest = getOtherChestHalf(block);
if (otherChest != null) {
lockedBlocks.remove(otherChest.getLocation());
}
saveLocks();
return; return;
} }
@@ -233,6 +437,13 @@ public class LockSystem implements Listener, CommandExecutor {
player.sendMessage(plugin.getMessage("lock.no-permission-unlock")); player.sendMessage(plugin.getMessage("lock.no-permission-unlock"));
} else { } else {
lockedBlocks.remove(loc); lockedBlocks.remove(loc);
// Entferne auch Lock von der anderen Hälfte bei Doppeltruhen
Block otherChest = getOtherChestHalf(targetBlock);
if (otherChest != null) {
lockedBlocks.remove(otherChest.getLocation());
}
saveLocks(); saveLocks();
player.sendMessage(plugin.getMessage("lock.unlocked")); player.sendMessage(plugin.getMessage("lock.unlocked"));
} }
@@ -254,6 +465,16 @@ public class LockSystem implements Listener, CommandExecutor {
player.sendMessage(plugin.getMessage("lock.friendadd.not-found")); player.sendMessage(plugin.getMessage("lock.friendadd.not-found"));
} else { } else {
lock.addFriend(friend.getUniqueId().toString()); lock.addFriend(friend.getUniqueId().toString());
// Füge Friend auch zur anderen Hälfte bei Doppeltruhen hinzu
Block otherChest = getOtherChestHalf(targetBlock);
if (otherChest != null) {
LockData otherLock = lockedBlocks.get(otherChest.getLocation());
if (otherLock != null) {
otherLock.addFriend(friend.getUniqueId().toString());
}
}
saveLocks(); saveLocks();
player.sendMessage(plugin.getMessage("lock.friendadd.success").replace("{player}", friend.getName())); player.sendMessage(plugin.getMessage("lock.friendadd.success").replace("{player}", friend.getName()));
} }
@@ -276,6 +497,16 @@ public class LockSystem implements Listener, CommandExecutor {
player.sendMessage(plugin.getMessage("lock.friendremove.not-found")); player.sendMessage(plugin.getMessage("lock.friendremove.not-found"));
} else { } else {
lock.removeFriend(friend.getUniqueId().toString()); lock.removeFriend(friend.getUniqueId().toString());
// Entferne Friend auch von der anderen Hälfte bei Doppeltruhen
Block otherChest = getOtherChestHalf(targetBlock);
if (otherChest != null) {
LockData otherLock = lockedBlocks.get(otherChest.getLocation());
if (otherLock != null) {
otherLock.removeFriend(friend.getUniqueId().toString());
}
}
saveLocks(); saveLocks();
player.sendMessage(plugin.getMessage("lock.friendremove.success").replace("{player}", friend.getName())); player.sendMessage(plugin.getMessage("lock.friendremove.success").replace("{player}", friend.getName()));
} }

View File

View File

@@ -1,3 +1,13 @@
# Version (Nicht Ändern!)
version: 1.1.0
# Debug-Option
debug-logging: false
# Spieler Kopf bei Tod
heads:
drop-on-death: true
# Neulings Schutz # Neulings Schutz
newbie-protection: newbie-protection:
enabled: true enabled: true

View File

@@ -1,306 +1,364 @@
header: "&6=== SurvivalPlus Hilfe ===" header: "&6=== SurvivalPlus Hilfe ==="
footer: "&6===========================" footer: "&6==========================="
help-usage: "&cVerwendung: /sp help [player|admin|system|all] [seite]"
navigation:
prev: "&7« Vorherige Seite"
next: "&7Nächste Seite »"
prev-disabled: "&8« Vorherige Seite"
next-disabled: "&8Nächste Seite »"
page: "&fSeite {current} von {total}"
groups:
system:
title: "&6-- System --"
player:
title: "&6-- Spieler --"
admin:
title: "&6-- Admin --"
commands: commands:
gm:
description: "&eÄndert den Spielmodus eines Spielers (survival, creative, adventure, spectator)."
usage: "&b/gm <modus> [spieler]"
sp: sp:
description: "&eHauptbefehl für SurvivalPlus mit Unterbefehlen." group: system
usage: "&b/sp [reload | help | info | share | lock]" description: "&eHauptbefehl für SurvivalPlus."
usage: "&b/sp [help|info|share|cb|reload]"
sp_reload:
description: "&eLädt das Plugin neu."
usage: "&b/sp reload"
sp_help: sp_help:
description: "&eZeigt die Hilfe für SurvivalPlus-Befehle an." group: system
usage: "&b/sp help" description: "&eZeigt die Hilfe an (Spieler/Admin/System)."
usage: "&b/sp help [player|admin|system|all] [seite]"
sp_info: sp_info:
description: "&eZeigt Informationen über das Plugin an." group: system
description: "&eZeigt Plugin-Infos und Links."
usage: "&b/sp info" usage: "&b/sp info"
sp_share: sp_share:
description: "&eTeilt deine Koordinaten nach Bestätigung mit allen Spielern." group: player
description: "&eTeilt deine Koordinaten nach Bestätigung."
usage: "&b/sp share" usage: "&b/sp share"
shareconfirm:
group: player
description: "&eBestätigt das Teilen der Koordinaten."
usage: "&b/sp shareconfirm"
sharecancel:
group: player
description: "&eBricht das Teilen der Koordinaten ab."
usage: "&b/sp sharecancel"
sp_cb_add: sp_cb_add:
group: admin
description: "&eFügt einen Befehl zur Blockierliste hinzu." description: "&eFügt einen Befehl zur Blockierliste hinzu."
usage: "&b/sp cb add <befehl>" usage: "&b/sp cb add <befehl>"
sp_cb_remove: sp_cb_remove:
group: admin
description: "&eEntfernt einen Befehl aus der Blockierliste." description: "&eEntfernt einen Befehl aus der Blockierliste."
usage: "&b/sp cb remove <befehl>" usage: "&b/sp cb remove <befehl>"
sp_cb_list: sp_cb_list:
group: admin
description: "&eZeigt die Liste der blockierten Befehle an." description: "&eZeigt die Liste der blockierten Befehle an."
usage: "&b/sp cb list" usage: "&b/sp cb list"
sp_reload:
group: admin
description: "&eLädt das Plugin neu."
usage: "&b/sp reload"
splock:
group: player
description: "&eSperrt/entsperrt Container und verwaltet Freunde."
usage: "&b/splock lock|unlock|friendadd|friendremove [spieler]"
sp_lock_lock: sp_lock_lock:
description: "&eSperrt einen Container (z.B. Kiste oder Tür)." group: player
usage: "&b/sp lock " description: "&eStartet den Lock-Modus (Block anklicken)."
usage: "&b/sp lock"
sp_lock_unlock: sp_lock_unlock:
description: "&eEntsperrt einen Container (z.B. Kiste oder Tür)." group: player
usage: "&b/sp lock " description: "&eEntsperrt einen Block, den du ansiehst."
usage: "&b/sp unlock"
sp_lock_friendadd: sp_lock_friendadd:
description: "&eFügt einen Freund zum Container-Sperrsystem hinzu." group: player
usage: "&b/sp lock friendadd <Spieler>" description: "&eFügt einen Freund zum Lock-System hinzu."
usage: "&b/sp friendadd <spieler>"
sp_lock_friendremove: sp_lock_friendremove:
description: "&eEntfernt einen Freund aus dem Container-Sperrsystem." group: player
usage: "&b/sp lock friendremove <Spieler>" description: "&eEntfernt einen Freund aus dem Lock-System."
usage: "&b/sp friendremove <spieler>"
shareconfirm: home:
description: "&eBestätigt das Teilen deiner Koordinaten mit allen Spielern." group: player
usage: "&b/sp shareconfirm" description: "&eTeleportiert dich zu einem Home."
usage: "&b/home <name>"
sharecancel:
description: "&eBricht das Teilen deiner Koordinaten ab."
usage: "&b/sp sharecancel"
sethome: sethome:
description: "&eSetzt ein Home mit dem angegebenen Namen." group: player
description: "&eSetzt ein Home mit Namen."
usage: "&b/sethome <name>" usage: "&b/sethome <name>"
delhome: delhome:
description: "&eLöscht ein Home mit dem angegebenen Namen." group: player
description: "&eLöscht ein Home."
usage: "&b/delhome <name>" usage: "&b/delhome <name>"
homelist: homelist:
description: "&eÖffnet eine GUI mit allen Homes." group: player
description: "&eÖffnet die Homes-Übersicht."
usage: "&b/homelist" usage: "&b/homelist"
home: warps:
description: "&eTeleportiert zu einem Home." group: player
usage: "&b/home <name>" description: "&eÖffnet die Warps-Übersicht."
usage: "&b/warps"
inv: setwarp:
description: "&eÖffnet das Inventar (eigenes oder das eines anderen Spielers)." group: player
usage: "&b/inv [spieler]" description: "&eSetzt einen persönlichen Warp."
usage: "&b/setwarp <name>"
ec: delwarp:
description: "&eÖffnet die Endertruhe (eigene oder die eines anderen Spielers)." group: player
usage: "&b/ec [spieler]" description: "&eLöscht einen persönlichen Warp."
usage: "&b/delwarp <name>"
setworldspawn: spawn:
description: "&eSetzt den Weltspawnpunkt auf die Position des Spielers." group: player
usage: "&b/setworldspawn" description: "&eTeleportiert dich zum Weltspawn."
usage: "&b/spawn"
setspawn:
description: "&eSetzt den Server-Spawnpunkt auf die Position des Spielers."
usage: "&b/setspawn"
clearchat:
description: "&eLöscht den Chat für alle Spieler."
usage: "&b/clearchat"
clearitems:
description: "&eLöscht alle herumliegenden Items."
usage: "&b/clearitems"
closedoors:
description: "&eSchließt alle Türen im angegebenen Radius."
usage: "&b/closedoors <radius>"
sit:
description: "&eLässt den Spieler sich hinsetzen oder aufstehen."
usage: "&b/sit"
back: back:
group: player
description: "&eTeleportiert zum letzten Todespunkt." description: "&eTeleportiert zum letzten Todespunkt."
usage: "&b/back" usage: "&b/back"
friend:
description: "&eVerwaltet die Freundesliste (hinzufügen, entfernen, anzeigen, teleportieren)."
usage: "&b/friend [add | accept | deny | list | del | tp] [Spieler]"
subcommands:
add:
description: "&eFügt einen Spieler zur Freundesliste hinzu."
usage: "&b/friend add <Spieler>"
accept:
description: "&eAkzeptiert eine Freundschaftsanfrage."
usage: "&b/friend accept <Spieler>"
deny:
description: "&eLehnt eine Freundschaftsanfrage ab."
usage: "&b/friend deny <Spieler>"
list:
description: "&eZeigt die Freundesliste an."
usage: "&b/friend list"
del:
description: "&eEntfernt einen Spieler aus der Freundesliste."
usage: "&b/friend del <Spieler>"
tp:
description: "&eTeleportiert dich zu einem Freund."
usage: "&b/friend tp <Spieler>"
ir:
description: "&eBenennt das Item in der Hand um."
usage: "&b/ir <neuer_name>"
showarmorstands:
description: "&eMacht alle unsichtbaren Armor Stands sichtbar."
usage: "&b/showarmorstands"
cleardebugarmorstands:
description: "&eEntfernt alle Debug-ArmorStands."
usage: "&b/cleardebugarmorstands"
trash:
description: "&eÖffnet den Mülleimer."
usage: "&b/trash"
workbench:
description: "&eÖffnet eine Werkbank-GUI."
usage: "&b/workbench"
anvil:
description: "&eÖffnet eine Amboss-GUI."
usage: "&b/anvil"
stats:
description: "&eZeigt deine Statistiken an."
usage: "&b/stats"
spawn:
description: "&eTeleportiert dich zum Weltspawnpunkt."
usage: "&b/spawn"
tp:
description: "&eTeleportiert dich zu einem Spieler."
usage: "&b/tp <Spieler>"
tphere:
description: "&eTeleportiert einen Spieler zu dir."
usage: "&b/tphere <Spieler>"
tpa: tpa:
description: "&eSendet eine Teleportanfrage an einen Spieler." group: player
usage: "&b/tpa <Spieler>" description: "&eSendet eine Teleportanfrage."
usage: "&b/tpa <spieler>"
tpaccept: tpaccept:
group: player
description: "&eAkzeptiert eine Teleportanfrage." description: "&eAkzeptiert eine Teleportanfrage."
usage: "&b/tpaccept" usage: "&b/tpaccept"
tpdeny: tpdeny:
group: player
description: "&eLehnt eine Teleportanfrage ab." description: "&eLehnt eine Teleportanfrage ab."
usage: "&b/tpdeny" usage: "&b/tpdeny"
friend:
group: player
description: "&eFreundesliste verwalten."
usage: "&b/friend [add|accept|deny|list|del|confirm|tp] [spieler]"
block: block:
description: "&eBlockiert einen Spieler." group: player
usage: "&b/block <Spieler>" description: "&eBlockiert einen Spieler im Chat."
usage: "&b/block <spieler>"
unblock: unblock:
description: "&eEntblockt einen Spieler." group: player
usage: "&b/unblock <Spieler>" description: "&eEntblockt einen Spieler im Chat."
usage: "&b/unblock <spieler>"
blocklist: blocklist:
description: "&eZeigt eine Liste der blockierten Spieler." group: player
description: "&eZeigt deine Blockliste."
usage: "&b/blocklist" usage: "&b/blocklist"
trade:
group: player
description: "&eStartet einen Handel."
usage: "&b/trade <spieler>"
tradeaccept:
group: player
description: "&eAkzeptiert einen Handel."
usage: "&b/tradeaccept <spieler>"
report:
group: player
description: "&eMeldet einen Spieler an Admins."
usage: "&b/report <spieler> [grund]"
stats:
group: player
description: "&eZeigt deine Statistiken."
usage: "&b/stats"
kit: kit:
group: player
description: "&eHolt das Starterkit." description: "&eHolt das Starterkit."
usage: "&b/kit" usage: "&b/kit"
nick:
group: player
description: "&eSetzt deinen Nickname."
usage: "&b/nick <name> | off"
ride:
group: player
description: "&eReite einen Spieler oder Mob."
usage: "&b/ride [spieler]"
sit:
group: player
description: "&eHinsetzen/Aufstehen."
usage: "&b/sit"
ir:
group: player
description: "&eBenennt das Item in der Hand um."
usage: "&b/ir <neuer_name>"
trash:
group: player
description: "&eÖffnet den Mülleimer."
usage: "&b/trash"
workbench:
group: player
description: "&eÖffnet eine Werkbank-GUI."
usage: "&b/workbench"
anvil:
group: player
description: "&eÖffnet eine Amboss-GUI."
usage: "&b/anvil"
leashcount: leashcount:
description: "&eZeigt die Anzahl der geleinten Tiere an." group: player
description: "&eZeigt die Anzahl geleinter Tiere."
usage: "&b/leashcount" usage: "&b/leashcount"
nick:
description: "&eÄndert deinen Nicknamen mit Farb- und Hex-Support."
usage: "&b/nick <Name>"
lootchests: lootchests:
description: "&eZeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick teleportieren." group: admin
description: "&eListet aktive Loot-Kisten."
usage: "&b/lootchests" usage: "&b/lootchests"
tploot: tploot:
description: "&eTeleportiert dich zu einer Loot-Kiste (nur Admins)." group: admin
description: "&eTeleportiert zu einer Loot-Kiste."
usage: "&b/tploot <welt> <x> <y> <z>" usage: "&b/tploot <welt> <x> <y> <z>"
shop:
group: player
description: "&eShop GUI (Admin: /shop add ...)."
usage: "&b/shop [gui|add]"
claim:
group: player
description: "&eClaim-System verwalten."
usage: "&b/claim [mark|unclaim|trust|untrust|info|kick|ban|unban]"
startchallenge:
group: admin
description: "&eStartet eine Fun-Challenge."
usage: "&b/startchallenge <name>"
head:
group: player
description: "&eGibt dir den Kopf eines Spielers."
usage: "&b/head <spieler>"
heal:
group: admin
description: "&eHeilt einen Spieler vollständig."
usage: "&b/heal [spieler]"
gm:
group: admin
description: "&eÄndert den Spielmodus."
usage: "&b/gm <modus> [spieler]"
tp:
group: admin
description: "&eTeleportiert dich zu einem Spieler."
usage: "&b/tp <spieler>"
tphere:
group: admin
description: "&eTeleportiert einen Spieler zu dir."
usage: "&b/tphere <spieler>"
inv:
group: admin
description: "&eÖffnet ein Inventar (eigen/anderer)."
usage: "&b/inv [spieler]"
ec:
group: admin
description: "&eÖffnet eine Endertruhe (eigene/andere)."
usage: "&b/ec [spieler]"
setworldspawn:
group: admin
description: "&eSetzt den Weltspawn auf deine Position."
usage: "&b/setworldspawn"
setspawn:
group: admin
description: "&eSetzt den Serverspawn auf deine Position."
usage: "&b/setspawn"
clearchat:
group: admin
description: "&eLeert den Chat."
usage: "&b/clearchat"
clearitems:
group: admin
description: "&eEntfernt herumliegende Items."
usage: "&b/clearitems"
closedoors:
group: admin
description: "&eSchließt Türen im Radius."
usage: "&b/closedoors <radius>"
showarmorstands:
group: admin
description: "&eMacht Debug-ArmorStands sichtbar."
usage: "&b/showarmorstands"
cleardebugarmorstands:
group: admin
description: "&eEntfernt Debug-ArmorStands."
usage: "&b/cleardebugarmorstands"
vanish:
group: admin
description: "&eMacht dich unsichtbar."
usage: "&b/vanish"
freeze:
group: admin
description: "&eFriert einen Spieler ein."
usage: "&b/freeze <spieler>"
day: day:
group: admin
description: "&eSetzt die Zeit auf Tag." description: "&eSetzt die Zeit auf Tag."
usage: "&b/day" usage: "&b/day"
night: night:
group: admin
description: "&eSetzt die Zeit auf Nacht." description: "&eSetzt die Zeit auf Nacht."
usage: "&b/night" usage: "&b/night"
trade:
description: "&eStartet einen Handel mit einem Spieler."
usage: "&b/trade <Spieler>"
tradeaccept:
description: "&eAkzeptiert eine Handelsanfrage."
usage: "&b/tradeaccept <Spieler>"
report:
description: "&eMeldet einen Spieler an die Admins."
usage: "&b/report <Spieler> [Grund]"
showreport: showreport:
description: "&eZeigt alle Reports eines Spielers an." group: admin
usage: "&b/showreport <Spieler>" description: "&eZeigt Reports eines Spielers."
usage: "&b/showreport <spieler>"
clearreport: clearreport:
description: "&eLöscht alle Reports eines Spielers." group: admin
usage: "&b/clearreport <Spieler>" description: "&eLöscht Reports eines Spielers."
usage: "&b/clearreport <spieler>"
shop:
description: "&eVerwaltet den Server-Shop (z.B. Items hinzufügen)."
usage: "&b/shop add <item> <basispreis> <lagerbestand>"
setwarp:
description: "&eSetzt einen persönlichen Warp mit dem Item in der Hand."
usage: "&b/setwarp <name>"
delwarp:
description: "&eLöscht einen persönlichen Warp."
usage: "&b/delwarp <name>"
warps:
description: "&eÖffnet die GUI mit allen Spieler-Warps."
usage: "&b/warps"
startchallenge:
description: "&eStartet eine Fun-Challenge."
usage: "&b/startchallenge <name>"
heal:
description: "&eHeilt einen Spieler vollständig."
usage: "&b/heal [spieler]"
messages:
header: "&6=== Befehle ==="
footer: "&6================"
navigation:
prev: "&7« Vorherige Seite"
next: "&7Nächste Seite »"
prev-disabled: "&8« Vorherige Seite"
next-disabled: "&8Nächste Seite »"
page: "&fSeite {current} von {total}"
sp:
invalid-subcommand: "&cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share|lock]"
no-permission: "&cDu hast keine Berechtigung für diesen Befehl!"
plugin:
reloaded: "&aSurvivalPlus wurde erfolgreich neu geladen!"
info:
header: "&7===== SurvivalPlus Info ====="
name: "&ePlugin-Name: &f"
version: "&eVersion: &f"
author: "&eErsteller: &f"
description: "&eBeschreibung:\\n&f"
footer: "&7=========================="
share:
preview-title: "&aDeine aktuellen Koordinaten wären:"
preview-format: "&e%player% &7teilt Koordinaten: &eX: %x%, Y: %y%, Z: %z% &7in Welt &e%world%"
send-button: "&a[✅ Senden]"
cancel-button: "&c[❌ Abbrechen]"
send-hover: "&aKlicke, um deine Koordinaten an alle zu senden."
cancel-hover: "&cKlicke, um das Senden abzubrechen."
sent: "&aKoordinaten gesendet."
cancelled: "&eSenden der Koordinaten abgebrochen."
help-not-found: "&cHilfedatei (help.yml) konnte nicht geladen werden!"

View File

@@ -1,7 +1,21 @@
reload:
success: "§aSurvivalPlus wurde erfolgreich neu geladen!"
invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [reload|help|info|share]"
player-only: "§cDieser Befehl ist nur für Spieler!"
not-found: "§cKeine Befehle verfügbar."
groups:
system: "&6-- System --"
player: "&6-- Spieler --"
admin: "&6-- Admin --"
sp: sp:
no-permission: "§cDu hast keine Berechtigung für diesen Befehl!" no-permission: "§cDu hast keine Berechtigung für diesen Befehl!"
plugin.reloaded: "§aSurvivalPlus wurde erfolgreich neu geladen!" plugin.reloaded: "§aSurvivalPlus wurde erfolgreich neu geladen!"
invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [ reload | help | info | share ]" invalid-subcommand: "§cUngültiger Unterbefehl! Verwendung: /sp [help|info|share|shareconfirm|sharecancel|cb|reload]"
help-not-found: "§cHilfedatei (help.yml) konnte nicht geladen werden!" help-not-found: "§cHilfedatei (help.yml) konnte nicht geladen werden!"
share: share:
@@ -160,7 +174,7 @@ friend:
notify: "&c%s hat deine Freundschaftsanfrage abgelehnt." notify: "&c%s hat deine Freundschaftsanfrage abgelehnt."
list: list:
header: "&6=== Deine Freundesliste ===" header: "&6== Deine Freundesliste =="
entry: "&e%s: %s" entry: "&e%s: %s"
entry-offline: "&e%s: %s &7(Zuletzt online: %s)" entry-offline: "&e%s: %s &7(Zuletzt online: %s)"
online: "&aOnline" online: "&aOnline"
@@ -168,7 +182,7 @@ friend:
unknown: "&7Unbekannt" unknown: "&7Unbekannt"
date-format: "dd.MM.yyyy HH:mm:ss" date-format: "dd.MM.yyyy HH:mm:ss"
remove-button: "&c[X]" remove-button: "&c[X]"
footer: "&6=====================" footer: "&6======================="
del: del:
success: "&a%s wurde aus deiner Freundesliste entfernt." success: "&a%s wurde aus deiner Freundesliste entfernt."
@@ -402,5 +416,33 @@ inventory:
no-permission-others: "§cDu hast keine Berechtigung, das Inventar anderer Spieler anzusehen!" no-permission-others: "§cDu hast keine Berechtigung, das Inventar anderer Spieler anzusehen!"
player-only: "§cDieser Befehl ist nur für Spieler!" player-only: "§cDieser Befehl ist nur für Spieler!"
claim:
usage: "&cVerwendung: /claim mark <1|2> oder /claim"
points-not-set: "&cDu musst zuerst zwei Punkte markieren! Verwende /claim mark <1|2>."
different-worlds: "&cDie markierten Punkte müssen in derselben Welt liegen!"
too-large: "&cDer Bereich ist zu groß! Maximal erlaubt: %max% Blöcke²."
overlap: "&cDieser Bereich überschneidet sich mit einem bestehenden Claim!"
max-reached: "&cDu hast das Maximum von %max% Claims erreicht!"
success: "&aBereich beansprucht! (%count%/%max%)"
unclaimed: "&aBereich freigegeben!"
not-owner: "&cDies ist nicht dein Claim!"
point1-set: "&aPunkt 1 gesetzt bei x=%x%, z=%z%."
point2-set: "&aPunkt 2 gesetzt bei x=%x%, z=%z%."
trusted: "&a%player% ist jetzt in diesem Claim vertraut!"
untrusted: "&a%player% ist in diesem Claim nicht mehr vertraut!"
no-break: "&cDu kannst in diesem beanspruchten Bereich keine Blöcke abbauen!"
no-place: "&cDu kannst in diesem beanspruchten Bereich keine Blöcke platzieren!"
op-unclaimed: "&a%count% Claims von %player% wurden entfernt!"
no-claims-found: "&cKeine Claims für %player% gefunden!"
no-claim-at-location: "&cKein Claim an dieser Position!"
info:
- "&eClaim-Info:"
- "&7Besitzer: &e%owner%"
- "&7Welt: &e%world%"
- "&7Koordinaten: &eX1: %x1%, Z1: %z1% bis X2: %x2%, Z2: %z2%"
enter: "&aDu hast das Gebiet von %owner% betreten."
leave: "&eDu hast das Gebiet von %owner% verlassen."
force-survival: force-survival:
join-message: "§aDu wurdest in den Survivalmodus gesetzt!" join-message: "§aDu wurdest in den Survivalmodus gesetzt!"

View File

@@ -1,12 +1,9 @@
name: SurvivalPlus name: SurvivalPlus
<<<<<<< HEAD version: 1.1.3
version: 1.0.7
=======
version: 1.0.6
>>>>>>> aadc1e75cc9b97929c3cde4fa3d098d281ba32f7
main: de.viper.survivalplus.SurvivalPlus main: de.viper.survivalplus.SurvivalPlus
api-version: 1.21 api-version: 1.21
softdepend: [LuckPerms, PlaceholderAPI] softdepend: [LuckPerms, PlaceholderAPI, ProtocolLib, Vault]
author: Viper author: Viper
description: A plugin for enhancing survival gameplay in Minecraft. description: A plugin for enhancing survival gameplay in Minecraft.
@@ -95,8 +92,7 @@ commands:
friend: friend:
description: Verwaltet die Freundesliste description: Verwaltet die Freundesliste
usage: /<command> [add|accept|deny|list|del|tp] [Spielername] usage: /<command> [add|accept|deny|list|del|tp] [Spielername]
permission: survivalplus.friend # FIX: permission und permission-message entfernt um Warnung zu verhindern
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
stats: stats:
description: Zeigt deine Statistiken an description: Zeigt deine Statistiken an
usage: /<command> usage: /<command>
@@ -187,6 +183,21 @@ commands:
usage: /<command> <Name> usage: /<command> <Name>
permission: survivalplus.nick permission: survivalplus.nick
permission-message: "§cDu hast keine Berechtigung, deinen Nick zu ändern!" permission-message: "§cDu hast keine Berechtigung, deinen Nick zu ändern!"
ride:
description: Reite einen Spieler
usage: /<command> [spieler]
permission: survivalplus.ride
permission-message: "§cDu hast keine Berechtigung, Spieler zu reiten!"
vanish:
description: Macht dich unsichtbar
usage: /<command>
permission: survivalplus.vanish
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
freeze:
description: Friert einen Spieler ein
usage: /<command> <Spieler>
permission: survivalplus.freeze
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
lootchests: lootchests:
description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren. description: Zeigt eine Liste aller aktiven Loot-Kisten an. Admins können per Klick zu einer Kiste teleportieren.
usage: /<command> usage: /<command>
@@ -267,6 +278,19 @@ commands:
usage: /<command> [spieler] usage: /<command> [spieler]
permission: survivalplus.heal permission: survivalplus.heal
permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!" permission-message: "§cDu hast keine Berechtigung, Spieler zu heilen!"
claim:
description: Manages claims for anti-griefing
usage: /<command> [mark|unclaim|del|delete|trust|untrust|info|kick|ban|unban]
aliases: [cl]
permission: survivalplus.claim.use
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
# --- NEU: HEAD COMMAND ---
head:
description: Hol dir den Kopf eines Spielers.
usage: /<command> <name>
permission: survivalplus.head
permission-message: "§cDu hast keine Berechtigung für diesen Befehl."
permissions: permissions:
survivalplus.*: survivalplus.*:
@@ -318,6 +342,10 @@ permissions:
survivalplus.blocklist: true survivalplus.blocklist: true
survivalplus.kit: true survivalplus.kit: true
survivalplus.nick: true survivalplus.nick: true
survivalplus.ride: true
survivalplus.ride.exempt: true
survivalplus.vanish: true
survivalplus.freeze: true
survivalplus.lootchests: true survivalplus.lootchests: true
survivalplus.day: true survivalplus.day: true
survivalplus.night: true survivalplus.night: true
@@ -336,6 +364,14 @@ permissions:
survivalplus.heal.others: true survivalplus.heal.others: true
survivalplus.notify: true survivalplus.notify: true
survivalplus.chunkanimals: true survivalplus.chunkanimals: true
survivalplus.claim.use: true
survivalplus.claim.trust: true
survivalplus.claim.kick: true
survivalplus.claim.ban: true
survivalplus.head: true
survivalplus.claim.admin: true
survivalplus.vanish.silent: true
survivalplus.vanish.no-pickup: true
survivalplus.commandblocker.add: survivalplus.commandblocker.add:
description: Erlaubt das Hinzufügen von Befehlen zur Blockierliste description: Erlaubt das Hinzufügen von Befehlen zur Blockierliste
default: op default: op
@@ -471,6 +507,18 @@ permissions:
survivalplus.nick: survivalplus.nick:
description: Erlaubt das Ändern des eigenen Nicknamens (mit Farben & Hex) description: Erlaubt das Ändern des eigenen Nicknamens (mit Farben & Hex)
default: op default: op
survivalplus.ride:
description: Erlaubt das Reiten von Spielern
default: op
survivalplus.ride.exempt:
description: Spieler mit dieser Permission können nicht geritten werden
default: op
survivalplus.vanish:
description: Erlaubt das Unsichtbar werden
default: op
survivalplus.freeze:
description: Erlaubt das Einfrieren von Spielern
default: op
survivalplus.lootchests: survivalplus.lootchests:
description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten description: Erlaubt das Verwalten und Teleportieren zu Loot-Kisten
default: op default: op
@@ -525,3 +573,27 @@ permissions:
survivalplus.chunkanimals: survivalplus.chunkanimals:
description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk description: Erlaubt das Anzeigen der Anzahl der Tiere im aktuellen Chunk
default: op default: op
survivalplus.claim.use:
description: Erlaubt das Erstellen von Claims
default: true
survivalplus.claim.trust:
description: Erlaubt das Verwalten von Trusted-Spielern in Claims
default: true
survivalplus.claim.kick:
description: Erlaubt das Kicken von Spielern aus Claims
default: op
survivalplus.claim.ban:
description: Erlaubt das Bannen von Spielern aus Claims
default: op
survivalplus.claim.admin:
description: Erlaubt das Löschen von fremden Claims (Admin Feature)
default: op
survivalplus.vanish.silent:
description: Erlaubt stummes Einloggen
default: op
survivalplus.vanish.no-pickup:
description: Kein Items aufheben im Vanish-Modus
default: op
survivalplus.head:
description: Erlaubt das Holen von Spielerköpfen
default: true