Compare commits

..

169 Commits

Author SHA1 Message Date
D4rkr34lm 99f7610d3d . 2026-06-12 19:48:16 +02:00
D4rkr34lm 2c05f06e2b Merge branch 'main' into BauSystem/add-cannon-automation-tool
# Conflicts:
#	BauSystem/BauSystem_Main/build.gradle.kts
2026-06-12 17:21:08 +02:00
D4rkr34lm 9606c6bc0d . 2026-06-12 16:50:26 +02:00
YoyoNow 669235709e Fix WorldEditListener 2026-06-12 15:14:09 +02:00
YoyoNow 796e154c1c Fix paper in inventory 2026-06-12 15:07:46 +02:00
YoyoNow 9efe35fe0d Update LobbySystem run task 2026-06-12 14:52:46 +02:00
YoyoNow e0aaacd66a Fix AnvilGUI for 1.21 not being mojmapped 2026-06-12 14:52:03 +02:00
D4rkr34lm 34e7c62768 First draft of initial user flow 2026-06-12 13:08:35 +02:00
Chaoscaot 27877938bc Add support for GameMode templates and YAML-based configuration handling in DevCommand.
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-06-12 12:29:56 +02:00
YoyoNow 701964c2f2 Fix DevCommand.kt 2026-06-12 12:15:07 +02:00
YoyoNow 6cbfc94e6d Merge pull request 'Access widener for paper' (#420) from AccessWidener into main
Reviewed-on: SteamWar/SteamWar#420
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-06-12 11:16:33 +02:00
YoyoNow c0ff123279 Fix EntityDamage only applying cancel for Player 2026-06-12 11:09:13 +02:00
YoyoNow 5511a1f08e Fix steamwar.devserver.gradle 2026-06-12 10:41:18 +02:00
YoyoNow 8c8da355bb Fix build.yml 2026-06-12 10:02:34 +02:00
Chaoscaot 7326ed4aa0 Merge pull request 'Remove some deprecated code in favor of newer code' (#421) from RemoveDeprecatedCode into main
Reviewed-on: SteamWar/SteamWar#421
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-06-12 09:58:05 +02:00
YoyoNow 2d59eb81c7 Fix ColorInit and CustomMap deprecation warnings 2026-06-12 09:49:32 +02:00
YoyoNow 711920464a Remove some deprecated code in favor of newer code 2026-06-12 09:37:43 +02:00
YoyoNow 117dbd08ae Implement startup arguments into steamwar.devserver.gradle 2026-06-11 23:54:42 +02:00
YoyoNow b36464e69e Implement Velocity changes 2026-06-11 23:50:34 +02:00
YoyoNow f5ac006c0c Fix REntity.EntityDataPacketBuilder 2026-06-11 23:49:38 +02:00
YoyoNow 414bfbfe3c Fix ClassTransformer public constructor 2026-06-11 23:48:33 +02:00
YoyoNow c362a3e1f0 Remove unused code 2026-06-11 23:24:11 +02:00
YoyoNow 2b43af8851 Remove unneded reflection 2026-06-11 23:22:05 +02:00
YoyoNow 1590f8f0ee Remove Reflection 2026-06-11 23:18:22 +02:00
YoyoNow 932732737d Unreflect more stuff 2026-06-11 22:35:13 +02:00
YoyoNow 2bc164c1b0 Extract widener definition into a widener.gradle.kts plugin 2026-06-11 20:38:33 +02:00
YoyoNow aa66b3dc83 Optimize imports 2026-06-11 18:29:44 +02:00
YoyoNow f5d9c6e175 Unreflect everything 2026-06-11 18:26:15 +02:00
YoyoNow 36b31fac77 Remove unneeded code
Cleanup build.gradle.kts
2026-06-11 13:49:35 +02:00
YoyoNow 641cefad01 Add comments to bausystem.accesswidener 2026-06-11 13:46:55 +02:00
YoyoNow eb866125ea Remove reflection from BauSystem 2026-06-11 13:45:49 +02:00
YoyoNow 961331f029 Fix Agent and WideningTransformer and ClassPatcher 2026-06-11 13:19:04 +02:00
YoyoNow 786257ad0e Add initial AccessWidener 2026-06-11 12:22:44 +02:00
YoyoNow e176b3bca8 Fix steamwar.devserver.gradle 'worldName' parameter 2026-06-10 20:46:13 +02:00
YoyoNow f81f05c3d0 Merge pull request 'Add RedstoneEngineCommand' (#406) from BauSystem/RedstoneEngineCommand into main
Reviewed-on: SteamWar/SteamWar#406
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-06-10 09:04:15 +02:00
YoyoNow 0afb6ce06a Merge branch 'main' into BauSystem/RedstoneEngineCommand 2026-06-10 09:03:55 +02:00
YoyoNow a938abde3f Add 'debug' property to enable/disable debugger 2026-06-09 23:00:46 +02:00
YoyoNow ec9b0387c5 Fix permission for BlastResistanceCommand 2026-06-09 16:42:40 +02:00
YoyoNow 03c3d49659 Add BlastResistanceCommand 2026-06-09 16:38:22 +02:00
Chaoscaot 8d9a77ab67 Merge pull request 'chore(VelocityCore): reennable replays for serverteam' (#417) from wip/VelocityCore-reenable-replays-for-serverteam into main
Reviewed-on: SteamWar/SteamWar#417
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-06-07 00:06:07 +02:00
D4rkr34lm fc2997e011 Rennable replays for serverteam 2026-06-06 12:17:23 +02:00
D4rkr34lm 8eb5f5ddf2 fix(BauSystem): cursor not properly disapearing 2026-06-03 21:23:36 +02:00
YoyoNow aa807060f4 Merge pull request 'fix(SpigotCore): small collection of rendering bugs in new cursor' (#413) from BauSystem/fix-small-sim-cursor-bug into main
Reviewed-on: SteamWar/SteamWar#413
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 21:39:13 +02:00
D4rkr34lm 9bbbab9d4b Fix collection of rendering issues 2026-06-02 21:33:31 +02:00
YoyoNow 7cca4ada10 Merge pull request 'refactor(BauSystem): Extract sim cursor into generic curser and refactor sim cursor' (#410) from BauSystem/refactor-sim-cursor-no2 into main
Reviewed-on: SteamWar/SteamWar#410
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 20:22:00 +02:00
YoyoNow 725e6df047 Merge pull request 'fix(SpigotCore): versions above 1.21.6 being kicked from arena' (#412) from SpigotCore/fix-newer-version-being-kicked-from-arena into main
Reviewed-on: SteamWar/SteamWar#412
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-06-02 20:19:57 +02:00
D4rkr34lm f3ce124a23 Make dev velocity easier to debug and fix version issue 2026-06-02 20:18:13 +02:00
D4rkr34lm e83f72a53e Further simplification 2026-06-01 22:01:52 +02:00
D4rkr34lm def0c19e43 Refactor simulation cursor to use new generic cursor 2026-06-01 21:51:54 +02:00
D4rkr34lm 3810ccd63d Add proper onclick handleing 2026-06-01 21:27:34 +02:00
D4rkr34lm a018af1c8a Refactor to use Player components for better usability 2026-05-31 13:20:24 +02:00
YoyoNow fd8f942014 Fix RedstoneEngine not registering 2026-05-31 11:37:43 +02:00
YoyoNow eab8826583 Fix RedstoneEngine not registering 2026-05-31 11:36:21 +02:00
YoyoNow bc0177df43 Add ExperimentalCommand 2026-05-31 00:56:05 +02:00
YoyoNow 1dc78b8eb8 Add RedstoneEngineCommand 2026-05-31 00:44:56 +02:00
YoyoNow a9fb982143 Merge pull request 'Fix WorldDir of Event servers' (#405) from VelocityCore/FixEventWorldDir into main
Reviewed-on: SteamWar/SteamWar#405
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-30 18:33:17 +02:00
YoyoNow 5b8d881e01 Fix WorldDir of Event servers 2026-05-30 18:30:51 +02:00
YoyoNow eb55f4b395 Merge pull request 'Remove global Entity Interact callback' (#404) from SpigotCore/RemoveGlobalEntityServerCallback into main
Reviewed-on: SteamWar/SteamWar#404
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-30 15:27:45 +02:00
YoyoNow 273db91d06 Fix compile 2026-05-30 15:24:20 +02:00
YoyoNow 983ad544c1 Remove global Entity Interact callback 2026-05-30 15:18:37 +02:00
YoyoNow a6a34b2221 Fix Schem add on WGS Schems 2026-05-30 14:13:08 +02:00
YoyoNow 6c6bd19038 Merge pull request 'fix(BauSystem): interaction with trace entity not showing details text' (#398) from BauSystem/fix-interaction-with-trace-entity into main
Reviewed-on: SteamWar/SteamWar#398
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-05-30 13:35:35 +02:00
D4rkr34lm 89e05cd109 Remove remnatn of old impl 2026-05-30 13:30:47 +02:00
D4rkr34lm ea9d7ac584 Refactor to use better impl of interaction entity in REntity system 2026-05-30 13:29:50 +02:00
D4rkr34lm 17e1cf53b0 Merge branch 'main' into BauSystem/fix-interaction-with-trace-entity 2026-05-30 13:24:43 +02:00
YoyoNow f64f337f17 Merge pull request 'Add RInteraction Entity' (#403) from SpigotCore/RInteraction into main
Reviewed-on: SteamWar/SteamWar#403
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-30 13:22:35 +02:00
YoyoNow c5f5be7d58 Add RInteraction Entity 2026-05-30 13:17:49 +02:00
Chaoscaot 9f18644763 Merge pull request 'fix(FightSystem): arrow stopper removing entites not shot by player (#399)' (#402) from wip/399-fix-error-stopper-for-dispensers into main
Reviewed-on: SteamWar/SteamWar#402
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-30 12:11:12 +02:00
D4rkr34lm 1de17d27f4 Fix arrow stopper 2026-05-30 11:55:59 +02:00
D4rkr34lm 3cd0db9bdf Merge branch 'main' into BauSystem/fix-interaction-with-trace-entity 2026-05-25 21:32:12 +02:00
D4rkr34lm 9711b48f2f Fix interaction with trace entity not showing detail text 2026-05-25 18:20:42 +02:00
Chaoscaot 9934b8bbb2 Fix TNTLeague InviteCommand.kt
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-25 01:55:20 +02:00
Chaoscaot 6447265b90 Merge pull request 'feat(BauSystem): use display entity to render trace' (#397) from BauSystem/use-display-entities-for-trace-render into main
Reviewed-on: SteamWar/SteamWar#397
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-25 01:40:30 +02:00
D4rkr34lm 71258318db Replace trace rendering by fallingblockentity with display entity for better accuracy 2026-05-24 23:39:50 +02:00
Chaoscaot 52e95e2649 Merge pull request 'fix(VelocityCore): disable replays' (#395) from disable-replays into main
Reviewed-on: SteamWar/SteamWar#395
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-24 13:45:32 +02:00
D4rkr34lm de6ac2cf20 fix 2026-05-24 13:44:57 +02:00
D4rkr34lm 628001e693 Disable Replays 2026-05-24 13:42:08 +02:00
Chaoscaot 47c148d64a Merge pull request 'fix(SpigotCore): techhider not hiding waterlogged blocks if neccecary' (#393) from fix-techhider-not-hidding-waterlogged-blocks into main
Reviewed-on: SteamWar/SteamWar#393
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-23 16:33:11 +02:00
D4rkr34lm 78617d5a98 another fix 2026-05-23 16:15:31 +02:00
D4rkr34lm 69b924ded6 fix techhider not hiding waterlogged blocks if neccecary 2026-05-23 12:29:19 +02:00
YoyoNow 002f7e5542 Merge pull request 'fix(FightSystem): techhider being active in check and test state' (#392) from fix-techhider-being-active-in-check-n-test-arena into main
Reviewed-on: SteamWar/SteamWar#392
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-05-23 06:23:16 +02:00
D4rkr34lm 5d70f75ac9 Move techhider init into enable to prevent always active even in check and test state 2026-05-22 23:12:39 +02:00
YoyoNow 7f21a31ec9 Merge pull request 'refactor(SpigotCore): Techhider to ensure safety and improve reviewablity' (#338) from FightSystem/fix-tech-and-hull-hider into main
Reviewed-on: SteamWar/SteamWar#338
2026-05-22 21:37:03 +02:00
YoyoNow 23b5ab3e82 Fix build 2026-05-22 21:35:01 +02:00
YoyoNow 4f9fe07951 Fix final formatting stuff 2026-05-22 21:29:58 +02:00
YoyoNow 3bcff4c5ce Merge pull request 'perf(FightSystem): increase chunk hiding performance by skipping irrelavant chunks' (#385) from FightSystem/Optimize-new-techhider into FightSystem/fix-tech-and-hull-hider
Reviewed-on: SteamWar/SteamWar#385
Reviewed-by: YoyoNow <4+yoyonow@noreply.localhost>
2026-05-22 20:52:11 +02:00
D4rkr34lm 1810cb7546 Fix inlineing 2026-05-22 20:51:24 +02:00
D4rkr34lm e55ca911c4 Inline get all regions 2026-05-22 20:41:05 +02:00
D4rkr34lm 793f2de6c3 Sync with base branch 2026-05-22 19:42:36 +02:00
D4rkr34lm 54fa47bd99 Ensure parity with old techhider by suppressing select packets 2026-05-22 19:39:57 +02:00
YoyoNow 14a770b207 Merge pull request 'fix(BauSystem): tick step bossbar remaining visable' (#391) from fix-390-fix-tickstep-bosbar-not-disapearing into main
Reviewed-on: SteamWar/SteamWar#391
2026-05-22 18:56:30 +02:00
D4rkr34lm 7b3a04f4eb Remove unused custom event 2026-05-22 18:49:51 +02:00
D4rkr34lm f2a06057a8 Fix tick step bossbar remaining visable 2026-05-22 18:46:36 +02:00
D4rkr34lm e11f3f7cbc Fix preschem state and techider not being always active 2026-05-22 18:27:53 +02:00
D4rkr34lm ebc10c1ce4 Fix constructor visiblity 2026-05-22 17:57:15 +02:00
D4rkr34lm d682e35159 Resolve open comments 2026-05-22 17:55:12 +02:00
D4rkr34lm 7d74eb0c09 add old techhider to skip supporting bausystem for now 2026-05-22 17:40:33 +02:00
D4rkr34lm 7e18207b87 fix entities not despawning 2026-05-22 16:32:49 +02:00
YoyoNow 72b23ad116 Add filling off Hull for reset and paste with either visible blocks or occluding blocks 2026-05-22 16:24:34 +02:00
YoyoNow c508627d92 Merge pull request 'fix(BauSystem): techhider bypass on bau by middleclick pick' (#389) from fix-387-bau-techhider-bypass-by-middleclick into main
Reviewed-on: SteamWar/SteamWar#389
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-22 10:17:19 +02:00
D4rkr34lm 93ff982649 fix techhider bypass on bau by middleclick pick 2026-05-21 23:57:38 +02:00
D4rkr34lm 21f6394359 Fix section packet being unnececarily reconstructed 2026-05-21 22:20:40 +02:00
D4rkr34lm 29d0c8978c Rmeove old player join quickfix 2026-05-21 22:00:15 +02:00
D4rkr34lm 7366f83b15 Merge branch 'FightSystem/fix-tech-and-hull-hider' into FightSystem/Optimize-new-techhider 2026-05-21 21:57:41 +02:00
YoyoNow 9240bcfd5f Add blockUsedForObfuscation 2026-05-21 20:57:59 +02:00
D4rkr34lm bede8caa82 Add chunk skipping optimisation 2026-05-21 20:20:18 +02:00
D4rkr34lm 108aa2af9a remove unused reflection class 2026-05-21 18:46:00 +02:00
D4rkr34lm d07a86c13f remove container hiding as it has no lacalaity mapping posibility 2026-05-21 18:41:55 +02:00
D4rkr34lm 29106d5bb5 Add bundle packet handling 2026-05-21 18:20:15 +02:00
YoyoNow 32fd4b4630 Add Sound handling 2026-05-21 18:11:42 +02:00
YoyoNow c241aef932 Add BlockEntityType handling 2026-05-21 17:53:51 +02:00
D4rkr34lm 2db24ef2eb Merge remote-tracking branch 'origin/FightSystem/fix-tech-and-hull-hider' into FightSystem/fix-tech-and-hull-hider 2026-05-21 15:08:41 +02:00
D4rkr34lm a2caa79f99 Erste bugs ausgemerzt 2026-05-21 15:06:33 +02:00
YoyoNow e49cfa9495 Fix duplicates in CouncilChannel 2026-05-21 13:19:27 +02:00
YoyoNow 1f44b3169e Remove unused stuff 2026-05-21 13:05:16 +02:00
YoyoNow 49fafa5955 Fix build 2026-05-21 13:00:53 +02:00
YoyoNow c81e6b15e5 Merge branch 'main' into FightSystem/fix-tech-and-hull-hider
# Conflicts:
#	FightSystem/FightSystem_Core/src/de/steamwar/fightsystem/utils/HullHider.java
#	SpigotCore/SpigotCore_Main/src/de/steamwar/core/events/AntiNocom.java
2026-05-21 12:59:22 +02:00
D4rkr34lm dc0365834a fixes 2026-05-21 12:13:41 +02:00
YoyoNow 13334e03a4 Add Debugger for remote JVM 2026-05-21 12:11:40 +02:00
YoyoNow b35b2c0138 Add jvmArgs to steamwar.devserver.gradle 2026-05-21 11:31:39 +02:00
YoyoNow 6ef311ce68 Merge pull request 'Fix CouncilChannel' (#384) from VelocityCore/FixDiscordBot into main
Reviewed-on: SteamWar/SteamWar#384
2026-05-21 11:23:06 +02:00
YoyoNow 68d9c55525 Downgrade JDA to 5.5.1 2026-05-21 11:21:35 +02:00
YoyoNow dcd70c5ced Fix CouncilChannel
Update JDA to 6.4.1
2026-05-21 11:20:37 +02:00
D4rkr34lm a1d22b7d77 twast 2026-05-21 10:26:46 +02:00
YoyoNow 3b6fdbeec3 Fix CouncilChannel 2026-05-21 10:16:17 +02:00
YoyoNow 714f86a55b Improve Tablist Header colors 2026-05-21 10:02:34 +02:00
YoyoNow a177cc4faf Fix Tablist
Add UpdateTeamsPacketImpl back
2026-05-21 09:54:48 +02:00
YoyoNow 09aa2ee22c Fix compile of VelocityCore 2026-05-21 08:43:28 +02:00
YoyoNow b8499a7a8a Fix Tablist 2026-05-21 08:36:24 +02:00
YoyoNow e50bff3d38 Merge pull request 'Unwrap the wrappers' (#381) from UnwrapWrapper into main
Reviewed-on: SteamWar/SteamWar#381
2026-05-21 07:56:16 +02:00
D4rkr34lm 8424f842c3 resolve last compile issue in techhider 2026-05-21 00:18:12 +02:00
YoyoNow 8f23f57415 Fix TeamCommand 2026-05-20 23:01:50 +02:00
YoyoNow 3b65b93e73 Merge pull request 'SW Prefix Implementation' (#383) from Jaki/SteamWar:main into main
Reviewed-on: SteamWar/SteamWar#383
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-20 22:47:46 +02:00
D4rkr34lm 72df919312 cut down old protocol wrapper 2026-05-20 21:47:17 +02:00
D4rkr34lm 7ed36fe56e removed reflections form anti nocom guard 2026-05-20 21:44:28 +02:00
D4rkr34lm a7a8c4d051 Fix imports in tinyProtocol 2026-05-20 21:33:01 +02:00
D4rkr34lm 5da3767378 Started embedding new techhider into fight system 2026-05-20 21:30:44 +02:00
D4rkr34lm ba29e5cf23 Add tinyProtocol Handler for techhider 2026-05-20 18:28:39 +02:00
Chaoscaot e94eac9b9d Fix Cauldrons in Schematic GUI
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-20 16:50:04 +02:00
YoyoNow a000beee20 Remove TinyProtocol.addTypedFilter 2026-05-20 13:52:13 +02:00
YoyoNow 2fd6c40b15 Remove some more wrappers 2026-05-20 13:27:49 +02:00
D4rkr34lm 32a2cbb4dd Finish initial implementation of logic 2026-05-19 22:47:22 +02:00
D4rkr34lm cf7a1ee086 Implement logic for hiding chunk data (not finished) 2026-05-19 22:17:41 +02:00
YoyoNow 8da7956523 Fix build 2026-05-19 21:03:38 +02:00
YoyoNow 9b821ff08f Unwrap the wrappers 2026-05-19 19:41:04 +02:00
YoyoNow ae6f279dcf Merge pull request 'add setting to disable block generators' (#375) from Jaki/SteamWar:main into main
Reviewed-on: SteamWar/SteamWar#375
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-19 19:25:25 +02:00
YoyoNow aead25650e Add debug output to TinyProtocol 2026-05-19 18:55:46 +02:00
YoyoNow 53ada2de83 Add debug output to TinyProtocol 2026-05-19 18:50:17 +02:00
YoyoNow 496cc3121c Add debug output to TinyProtocol 2026-05-19 18:41:15 +02:00
D4rkr34lm 270d82eb71 Merge branch 'main' into FightSystem/fix-tech-and-hull-hider 2026-05-17 19:58:16 +02:00
D4rkr34lm 9f319128cc Update multiblock update packet filter 2026-05-17 13:49:46 +02:00
D4rkr34lm 3bd1cf7167 Even more handlers 2026-05-16 22:12:25 +02:00
D4rkr34lm 723a7dc0ca Add handling for container 2026-05-16 20:14:09 +02:00
D4rkr34lm 88de28ed68 Add new handler 2026-05-16 19:25:32 +02:00
D4rkr34lm 6f1a3fe70c Update a lot of handlers and prepare for merge with main 2026-05-16 19:19:49 +02:00
D4rkr34lm 4a43e09a8b Add more handlers 2026-05-16 14:45:36 +02:00
D4rkr34lm 0452a9faa5 Add processors for a sed of id only entity packets 2026-05-15 16:23:23 +02:00
D4rkr34lm 9a75f38226 Continue 2026-05-15 16:14:05 +02:00
Manuel Frohn 42808f40c0 preclassify packets as safe 2026-05-15 00:48:45 +02:00
Manuel Frohn 57c21122e6 Classify packets by scope 2026-05-15 00:32:09 +02:00
Manuel Frohn 42faba70d9 Add remaining packets with toss handler for now 2026-05-14 22:08:17 +02:00
Manuel Frohn 99e1f01869 Update bypass list with harmless play-client bound packets 2026-05-14 21:38:37 +02:00
Manuel Frohn 13bddbe359 Update bypass list with full set of configuration and login packets 2026-05-14 20:21:36 +02:00
D4rkr34lm 51ff0e1a9f Fix remaining local issues 2026-05-14 11:57:26 +02:00
D4rkr34lm 6766862a00 Refactored over all functionality 2026-05-13 22:48:44 +02:00
Manuel Frohn c30aad179b Continue refactor 2026-05-13 17:20:32 +02:00
Manuel Frohn d3aad6a198 Move over signle block event package processing 2026-05-13 16:24:45 +02:00
Manuel Frohn 744dc67ca9 Move over identified packages from old impl 2026-05-13 15:33:08 +02:00
Manuel Frohn 164a7f5826 Replace tinyProtocolLib with newest version 2026-05-13 14:04:38 +02:00
168 changed files with 5647 additions and 2931 deletions
+1
View File
@@ -51,6 +51,7 @@ jobs:
rm -rf deploy
mkdir -p deploy
cp "AccessWidener/build/libs/AccessWidener-all.jar" "deploy/AccessWidener.jar"
cp "BauSystem/build/libs/BauSystem-all.jar" "deploy/BauSystem.jar"
cp "FightSystem/build/libs/FightSystem-all.jar" "deploy/FightSystem.jar"
cp "KotlinCore/build/libs/KotlinCore-all.jar" "deploy/KotlinCore.jar"
+15
View File
@@ -0,0 +1,15 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Remote JVM Debugger" type="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<option name="AUTO_RESTART" value="false" />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="5005" />
<option name="LOCAL" value="false" />
</RunnerSettings>
<method v="2" />
</configuration>
</component>
+45
View File
@@ -0,0 +1,45 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
plugins {
`java-library`
alias(libs.plugins.shadow)
}
dependencies {
implementation("org.ow2.asm:asm:9.7")
implementation("org.ow2.asm:asm-commons:9.7")
}
tasks.shadowJar {
manifest {
attributes(
"Manifest-Version" to "1.0",
"Build-Jdk-Spec" to "21",
"Main-Class" to "de.steamwar.Main",
"Premain-Class" to "de.steamwar.Agent",
"Can-Retransform-Classes" to "true",
"Can-Redefine-Classes" to "true",
)
}
}
tasks.build {
dependsOn(tasks.shadowJar)
}
@@ -0,0 +1,49 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
/**
* A single parsed line from a .accesswidener file.
* <p>
* Examples:
* accessible class net/minecraft/server/level/ServerPlayer
* accessible method net/minecraft/server/level/ServerPlayer getStats ()V
* mutable field net/minecraft/world/entity/Entity id I
* extendable class net/minecraft/world/level/chunk/LevelChunk
*/
public record AccessWidenerEntry(
/** accessible | mutable | extendable (may have "transitive-" prefix) */
String directive,
/** class | method | field */
String memberType,
/** Internal class name, e.g. net/minecraft/server/level/ServerPlayer */
String target,
/** Method/field name, null for class entries */
String name,
/** Descriptor, null for class entries */
String descriptor) {
/**
* Returns true if this entry targets the class with the given internal name.
*/
public boolean targets(String internalName) {
return target.equals(internalName);
}
}
@@ -0,0 +1,104 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* Parses Fabric-compatible .accesswidener files.
* <p>
* Supported format:
* <pre>
* accessWidener v2 named
*
* # comments are supported
* accessible class net/minecraft/Foo
* accessible method net/minecraft/Foo someMethod ()V
* accessible field net/minecraft/Foo someField I
* mutable field net/minecraft/Foo someField I
* extendable class net/minecraft/Foo
* extendable method net/minecraft/Foo someMethod ()V
*
* # transitive variants (expose widening to dependents)
* transitive-accessible class net/minecraft/Foo
* </pre>
*/
public final class AccessWidenerParser {
private AccessWidenerParser() {
}
public static List<AccessWidenerEntry> parse(InputStream in) throws IOException {
List<AccessWidenerEntry> entries = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
String line;
boolean headerSeen = false;
while ((line = reader.readLine()) != null) {
// Strip inline comments
int commentIdx = line.indexOf('#');
if (commentIdx >= 0) line = line.substring(0, commentIdx);
line = line.strip();
if (line.isEmpty()) continue;
if (!headerSeen) {
// First non-blank, non-comment line must be the header
if (!line.startsWith("accessWidener")) {
throw new IOException("Missing accessWidener header, got: " + line);
}
headerSeen = true;
continue;
}
AccessWidenerEntry entry = parseLine(line);
if (entry != null) entries.add(entry);
}
}
return entries;
}
private static AccessWidenerEntry parseLine(String line) {
String[] parts = line.split("\\s+");
if (parts.length < 3) return null;
String directive = parts[0]; // accessible / mutable / extendable / transitive-*
String memberType = parts[1]; // class / method / field
String target = parts[2]; // internal class name
return switch (memberType) {
case "class" -> new AccessWidenerEntry(directive, "class", target, null, null);
case "method", "field" -> {
if (parts.length < 5) yield null;
yield new AccessWidenerEntry(directive, memberType, target, parts[3], parts[4]);
}
default -> null;
};
}
}
@@ -0,0 +1,75 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Java agent entry point.
* <p>
* At JVM startup: java -javaagent:paper-access-widener-agent.jar -jar server.jar
* <p>
* On attach the agent:
* <ol>
* <li>Find all .jar files inside the plugins folder</li>
* <li>Scan all found jars for *.accesswidener resources</li>
* <li>Transform any class during loading</li>
* </ol>
*/
public class Agent {
private Agent() {
throw new IllegalStateException("Utility class");
}
private static final Logger LOG = Logger.getLogger("AccessWidenerAgent");
// -javaagent: startup
public static void premain(String args, Instrumentation inst) {
init(inst);
}
private static void init(Instrumentation inst) {
LOG.info("[AccessWidener] Agent initialising.");
List<AccessWidenerEntry> entries = new ArrayList<>();
File file = new File(new File(".").getAbsoluteFile(), "plugins/");
File[] files = file.listFiles();
if (files == null) files = new File[0];
for (File jarFile : files) {
if (!jarFile.isFile()) continue;
if (!jarFile.getName().endsWith(".jar")) continue;
try {
entries.addAll(Utils.findAndParseAccessWideners(jarFile.toPath()));
} catch (IOException e) {
LOG.warning("Failed to parse access wideners from " + jarFile.getAbsolutePath());
}
}
LOG.info("[AccessWidener] Loaded " + entries.size() + " access wideners.");
inst.addTransformer(new WideningTransformer(entries), false);
LOG.info("[AccessWidener] Agent ready.");
}
}
@@ -0,0 +1,83 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Uses ASM to patch class bytecode according to a list of access widener entries.
*
* Returns {@code null} if the class is not targeted by any entry (no-op signal
* to the caller so it can skip the write).
*/
public class ClassPatcher {
private static final Logger LOG = Logger.getLogger("ClassPatcher");
private final List<AccessWidenerEntry> entries;
/** Pre-computed set of targeted internal names for fast filtering. */
private final Set<String> targets;
private final Set<String> targetsPublicConstructor;
public ClassPatcher(List<AccessWidenerEntry> entries) {
this.entries = entries;
this.targets = entries.stream()
.map(AccessWidenerEntry::target)
.flatMap(s -> {
if (!s.contains("$")) return Stream.of(s);
int index = s.lastIndexOf('$');
return Stream.of(s, s.substring(0, index));
})
.collect(Collectors.toSet());
this.targetsPublicConstructor = entries.stream()
.filter(entry -> entry.directive().equals("transitive-extendable"))
.map(AccessWidenerEntry::target)
.collect(Collectors.toSet());
}
/**
* Patches {@code classBytes} if {@code className} is targeted.
*
* @return patched bytes, or {@code null} if no changes were needed
*/
public byte[] patch(String className, byte[] classBytes) {
if (!targets.contains(className)) return null;
try {
ClassReader cr = new ClassReader(classBytes);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassTransformer(cw, className, entries, targetsPublicConstructor.contains(className)), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage());
return null;
}
}
}
@@ -0,0 +1,125 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.List;
public class ClassTransformer extends ClassVisitor {
private final String internalName;
private final List<AccessWidenerEntry> entries;
private final boolean appendPublicConstructor;
public ClassTransformer(ClassVisitor cv, String internalName, List<AccessWidenerEntry> entries, boolean appendPublicConstructor) {
super(Opcodes.ASM9, cv);
this.internalName = internalName;
this.entries = entries;
this.appendPublicConstructor = appendPublicConstructor;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"class".equals(e.memberType())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
if (appendPublicConstructor) {
MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V",
false
);
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
super.visit(version, newAccess, name, signature, superName, interfaces);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.target().equals(name) || !"class".equals(e.memberType())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
super.visitInnerClass(name, outerName, innerName, newAccess);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"method".equals(e.memberType())) continue;
if (!name.equals(e.name()) || !descriptor.equals(e.descriptor())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
return super.visitMethod(newAccess, name, descriptor, signature, exceptions);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"field".equals(e.memberType())) continue;
if (!name.equals(e.name())) continue;
newAccess = applyDirective(e.directive(), newAccess, true);
}
return super.visitField(newAccess, name, descriptor, signature, value);
}
/**
* Apply a directive to an access bitmask.
*
* @param directive accessible / mutable / extendable (with optional "transitive-" prefix)
* @param access current access flags
* @param isField true when processing a field (mutable removes final)
*/
private static int applyDirective(String directive, int access, boolean isField) {
// Strip transitive- prefix — the widening itself is the same
String effective = directive.startsWith("transitive-") ? directive.substring("transitive-".length()) : directive;
return switch (effective) {
case "accessible" -> makePublic(access);
case "extendable" -> makePublic(removeFinal(access));
case "mutable" -> isField ? removeFinal(access) : access;
default -> access;
};
}
private static int makePublic(int access) {
return (access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC;
}
private static int removeFinal(int access) {
return access & ~Opcodes.ACC_FINAL;
}
}
@@ -0,0 +1,125 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
/**
* Command-line tool that produces a widened copy of a JAR for use as a
* compile-time stub in IntelliJ / Gradle.
*
* Usage:
* java -jar jar-widener.jar <input.jar> <output.jar> [file.accesswidener ...]
*
* The output JAR is identical to the input JAR except that every class
* targeted by the access widener entries has its access flags patched:
* accessible → public
* extendable → public + non-final
* mutable → non-final field
*
* Intended for use as a Gradle task so IntelliJ sees the already-widened
* class when you Ctrl+click NMS code, and javac compiles without complaints.
*/
public class Main {
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.err.println("Usage: jar-widener <input.jar> <output.jar> [*.accesswidener ...]");
System.exit(1);
}
Path inputJar = Path.of(args[0]);
Path outputJar = Path.of(args[1]);
if (!Files.exists(inputJar)) {
System.err.println("Input JAR not found: " + inputJar);
System.exit(1);
}
// --- Collect all access widener entries ---
List<AccessWidenerEntry> entries = new ArrayList<>();
if (args.length > 2) {
for (int i = 2; i < args.length; i++) {
Path awFile = Path.of(args[i]);
if (!Files.exists(awFile)) {
System.err.println("Warning: access widener file not found, skipping: " + awFile);
continue;
}
try (InputStream in = Files.newInputStream(awFile)) {
List<AccessWidenerEntry> parsed = AccessWidenerParser.parse(in);
System.out.println("Loaded " + parsed.size() + " entries from " + awFile.getFileName());
entries.addAll(parsed);
}
}
}
if (entries.isEmpty()) {
System.out.println("No access widener entries found — copying JAR unchanged.");
Files.createDirectories(outputJar.getParent());
Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
return;
}
System.out.println("Widening " + inputJar.getFileName()
+ " with " + entries.size() + " total entr"
+ (entries.size() == 1 ? "y" : "ies") + "...");
// --- Copy input → output, transforming .class files in place ---
Files.createDirectories(outputJar.getParent());
Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
ClassPatcher patcher = new ClassPatcher(entries);
try (FileSystem fs = FileSystems.newFileSystem(outputJar)) {
// Walk every .class entry in the JAR
try (var stream = Files.walk(fs.getPath("/"))) {
stream.filter(p -> p.toString().endsWith(".class"))
.forEach(classPath -> patchClass(fs, classPath, patcher));
}
}
System.out.println("Done. Widened JAR written to " + outputJar);
}
private static void patchClass(FileSystem fs, Path classPath, ClassPatcher patcher) {
// Derive internal class name from path e.g. /net/minecraft/Foo.class → net/minecraft/Foo
String internalName = classPath.toString()
.replaceFirst("^/", "")
.replace(".class", "");
try {
byte[] original = Files.readAllBytes(classPath);
byte[] patched = patcher.patch(internalName, original);
if (patched != null) {
Files.write(classPath, patched);
System.out.println(" Widened: " + internalName);
}
} catch (IOException e) {
System.err.println(" Warning: failed to patch " + internalName + ": " + e.getMessage());
}
}
}
@@ -0,0 +1,60 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class Utils {
private static final Logger LOG = Logger.getLogger("AccessWidenerAgent");
private Utils() {
throw new IllegalStateException("Utility class");
}
public static List<AccessWidenerEntry> findAndParseAccessWideners(Path jarPath) throws IOException {
List<AccessWidenerEntry> results = new ArrayList<>();
try (ZipFile zip = new ZipFile(jarPath.toFile())) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(".accesswidener")) continue;
try (InputStream in = zip.getInputStream(entry)) {
results.addAll(AccessWidenerParser.parse(in));
} catch (IOException e) {
LOG.warning("[AccessWidener] Failed to parse " + entry.getName()
+ " in " + jarPath.getFileName() + ": " + e.getMessage());
}
}
}
return results;
}
}
@@ -0,0 +1,44 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.List;
/**
* Transforms class bytecode to apply access widening rules.
* <p>
* Also monitors for new plugin ClassLoaders appearing (when plugins load after
* the agent attaches) and automatically picks up their .accesswidener files.
*/
public class WideningTransformer implements ClassFileTransformer {
private final ClassPatcher patcher;
public WideningTransformer(List<AccessWidenerEntry> entries) {
patcher = new ClassPatcher(entries);
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
return patcher.patch(className, classfileBuffer);
}
}
+6 -1
View File
@@ -18,7 +18,8 @@
*/
plugins {
steamwar.java
steamwar.kotlin
widener
}
tasks.compileJava {
@@ -47,3 +48,7 @@ dependencies {
implementation(libs.luaj)
implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
}
widener {
fromCatalog(libs.nms)
}
@@ -834,6 +834,10 @@ SKIN_NO_REGION = §7You are not in a region with a changealbe skin
SKIN_ALREADY_EXISTS = §cThis skin already exists like this
SKIN_MESSAGE = §7Skin created
SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Calculate min/max and average blast resistance of current clipboard
BLASTRESISTANCE_NO_CLIPBOARD = §cYou currently do not have a clipboard to be used.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection
PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor.
@@ -772,6 +772,10 @@ SKIN_NO_REGION = §7Du steht in keiner Region, welche mit einem Skin versehen we
SKIN_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits
SKIN_MESSAGE = §7Skin erstellt
SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Minimal-, Maximal- und durchschnittliche Sprengfestigkeit des aktuellen Inhalts der Zwischenablage berechnen
BLASTRESISTANCE_NO_CLIPBOARD = §cDerzeit steht Ihnen keine Zwischenablage zur Verfügung.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl
PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen.
@@ -0,0 +1,13 @@
accessWidener v2 named
# For NoClipCommand
accessible field net/minecraft/server/level/ServerPlayerGameMode gameModeForPlayer Lnet/minecraft/world/level/GameType;
# For PlaceItemUtils
accessible field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos;
mutable field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos;
accessible field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld;
mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld;
# For TickManager
accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J
@@ -22,6 +22,7 @@ package de.steamwar.bausystem;
import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.bausystem.configplayer.ConfigConverter;
import de.steamwar.bausystem.features.cannonCore.CannonCoreRegistrationKt;
import de.steamwar.bausystem.features.gui.BauGUI;
import de.steamwar.bausystem.features.script.lua.SteamWarLuaPlugin;
import de.steamwar.bausystem.features.script.lua.libs.LuaLib;
@@ -135,6 +136,8 @@ public class BauSystem extends JavaPlugin implements Listener {
String identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
CannonCoreRegistrationKt.register(this);
}
@EventHandler
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -17,20 +17,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.utils;
package de.steamwar.bausystem.features.cannonCore
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.Location
public class TickEndEvent extends Event {
class CannonCore(val location: Location) {
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
companion object Manager {
var activeCores: Observable<List<CannonCore>> = Observable(ArrayList())
}
}
@@ -0,0 +1,35 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import de.steamwar.bausystem.SWUtils
import de.steamwar.command.SWCommand
import de.steamwar.linkage.Linked
import org.bukkit.entity.Player
object CannonCoreCommand : SWCommand("cannoncore") {
@Register
fun giveCannonCoreWand(player: Player) {
val wandItem = CannonCoreWand.getWandItem()
SWUtils.giveItemToPlayer(player, wandItem)
}
}
@@ -0,0 +1,46 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import de.steamwar.entity.RBlockDisplay
import de.steamwar.entity.REntityAction
import de.steamwar.entity.REntityServer
import de.steamwar.entity.RInteraction
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.Player
class CannonCoreEntity: RBlockDisplay {
val entityMaterial = Material.COMMAND_BLOCK
val hitbox: RInteraction
constructor(server: REntityServer, location: Location, onClick: (player: Player, clickAction: REntityAction) -> Unit) : super(server, location) {
setBlock(entityMaterial.createBlockData())
hitbox = RInteraction(server, location)
hitbox.setCallback(onClick)
}
override fun die() {
super.die()
hitbox.die()
}
}
@@ -0,0 +1,29 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import org.bukkit.plugin.Plugin
fun register(plugin: Plugin) {
CannonCoreCommand.register()
val pluginManager = plugin.server.pluginManager
pluginManager.registerEvents(CannonCoreWand, plugin)
}
@@ -0,0 +1,64 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import de.steamwar.bausystem.utils.ItemUtils
import de.steamwar.core.SWPlayer
import de.steamwar.inventory.SWItem
import org.bukkit.Material
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerItemHeldEvent
import org.bukkit.inventory.ItemStack
object CannonCoreWand: Listener {
val wandId = "CANNON_CORE_WAND"
val wandMaterial = Material.BREEZE_ROD
fun getWandItem(): ItemStack {
val title = "§eCannon Core Wand"
val lore = listOf(
"§eRight Click §8- §7Create a new cannon core"
)
val wand = SWItem(wandMaterial, title, lore, false, null)
val item = wand.itemStack
return ItemUtils.setItem(item, wandId)
}
fun isWandItem(itemStack: ItemStack): Boolean {
return ItemUtils.isItem(itemStack, wandId)
}
@EventHandler
fun onPlayerEquip(event: PlayerItemHeldEvent) {
val player = event.player
val swPlayer = SWPlayer.of(event.player)
val item = player.inventory.getItem(event.newSlot) ?: return
if(!isWandItem(item)) {
swPlayer.removeComponent(EmptyCannonCoreWandDisplay::class.java)
}
else {
EmptyCannonCoreWandDisplay(player)
}
}
}
@@ -0,0 +1,45 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import de.steamwar.core.SWPlayer
import de.steamwar.entity.REntityServer
import org.bukkit.entity.Player
class CannonCoresDisplay: SWPlayer.Component {
val displayServer = REntityServer()
val unlisten: () -> Unit
constructor(owner: Player) {
unlisten = CannonCore.activeCores.observe( { cores ->
displayServer.entities.forEach { it.die() }
cores.forEach { CannonCoreEntity(displayServer, it.location) { _, _ -> println("Handler clicked at ${it.location}") } }
})
}
override fun onMount(player: SWPlayer) {
displayServer.addPlayer(player.player)
}
override fun onUnmount(player: SWPlayer?) {
unlisten()
displayServer.close()
}
}
@@ -0,0 +1,49 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
import de.steamwar.core.SWPlayer
import de.steamwar.cursor.Cursor
import org.bukkit.Material
import org.bukkit.entity.Player
class EmptyCannonCoreWandDisplay : SWPlayer.Component {
val coresDisplay: CannonCoresDisplay
val cursor: Cursor
constructor(owner: Player) {
coresDisplay = CannonCoresDisplay(owner)
cursor = Cursor(
coresDisplay.displayServer, owner, Material.GLASS, Material.COMMAND_BLOCK,
listOf(
Cursor.CursorMode.BLOCK_ALIGNED
),
) { location, hitEntity, action ->
print("Hello")
}
SWPlayer.of(owner).setComponent(this)
}
override fun onUnmount(player: SWPlayer) {
player.removeComponent(cursor::class.java)
}
}
@@ -0,0 +1,47 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.cannonCore
typealias Observer<T> = (currentValue: T) -> Unit
class Observable<T>(var value: T) {
private val observers = ArrayList<Observer<T>>()
fun set(value: T) {
val oldValue = this.value
this.value = value
observers.forEach { it.invoke(value) }
}
fun get(): T {
return value
}
fun observe(observer: Observer<T>): () -> Unit {
observers.add(observer)
observer(value)
return {removeObserver(observer)}
}
fun removeObserver(observer: Observer<T>) {
observers.remove(observer)
}
}
@@ -21,9 +21,7 @@ package de.steamwar.bausystem.features.design.endstone;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.entity.*;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -59,15 +57,15 @@ public class DesignEndStone {
.filter(material -> material.getBlastResistance() > region.getGameModeConfig().Schematic.MaxDesignBlastResistance)
.collect(Collectors.toSet());
calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;
}
entityServer.setCallback((player, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.ATTACK) return;
Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ());
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
location.getBlock().breakNaturally();
calc();
}, 1);
});
private void interact(Player player, RInteraction entity, REntityAction action) {
if (action != REntityAction.ATTACK) return;
Location location = new Location(WORLD, entity.getX(), entity.getY(), entity.getZ());
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
location.getBlock().breakNaturally();
calc();
}, 1);
}
public void calc() {
@@ -110,12 +108,15 @@ public class DesignEndStone {
Material material = WORLD.getBlockAt(cx, cy, cz).getType();
if (material != Material.WATER && material != Material.LAVA && limited.contains(material)) {
Location location = new Location(WORLD, cx + 0.5, cy, cz + 0.5);
Location location = new Location(WORLD, cx, cy, cz);
if (!locations.add(location)) break;
RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, location, Material.RED_STAINED_GLASS);
entity.setNoGravity(true);
RBlockDisplay entity = new RBlockDisplay(entityServer, location);
entity.setBlock(Material.RED_STAINED_GLASS.createBlockData());
entity.setGlowing(true);
entities.add(entity);
RInteraction interaction = new RInteraction(entityServer, location);
interaction.setCallback(this::interact);
entities.add(interaction);
break;
} else if (!material.isAir() && material != Material.WATER && material != Material.LAVA) {
break;
@@ -26,8 +26,9 @@ import de.steamwar.bausystem.features.autostart.AutostartListener;
import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
import de.steamwar.core.SWPlayer;
import de.steamwar.entity.RBlockDisplay;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.entity.RInteraction;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.UtilityClass;
@@ -58,10 +59,6 @@ public class Detonator {
@Override
public void onMount(SWPlayer player) {
entities.addPlayer(player.getPlayer());
entities.setCallback((player1, entity, action) -> {
Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ());
DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player1);
});
}
@Override
@@ -70,8 +67,6 @@ public class Detonator {
}
}
private static final Vector HALF = new Vector(0.5, 0, 0.5);
public static boolean isDetonator(ItemStack itemStack) {
return ItemStorage.isDetonator(itemStack);
}
@@ -79,8 +74,14 @@ public class Detonator {
public static void showDetonator(Player p, List<Location> locs) {
DetonatorComponent detonatorComponent = SWPlayer.of(p).getComponentOrDefault(DetonatorComponent.class, DetonatorComponent::new);
locs.forEach(location -> {
RFallingBlockEntity entity = new RFallingBlockEntity(detonatorComponent.entities, location.clone().add(HALF), Material.RED_STAINED_GLASS);
entity.setNoGravity(true);
RBlockDisplay blockDisplay = new RBlockDisplay(detonatorComponent.entities, location);
blockDisplay.setBlock(Material.RED_STAINED_GLASS.createBlockData());
RInteraction interaction = new RInteraction(detonatorComponent.entities, location);
interaction.setCallback((player, entity, action) -> {
Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ());
DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player);
});
});
}
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -17,20 +17,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.utils;
package de.steamwar.bausystem.features.experimental;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
public class TickStartEvent extends Event {
@Linked
public class ExperimentalCommand extends SWCommand {
private static final HandlerList handlers = new HandlerList();
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
public ExperimentalCommand() {
super("experimental", "experiment");
}
}
@@ -0,0 +1,135 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.experimental.redstone_engine;
import de.steamwar.bausystem.features.experimental.ExperimentalCommand;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import io.papermc.paper.configuration.WorldConfiguration;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.TitlePart;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.Collection;
import java.util.List;
@AbstractSWCommand.PartOf(ExperimentalCommand.class)
@Linked
public class RedstoneEngine extends SWCommand implements Listener, ScoreboardElement {
public RedstoneEngine() {
super("");
}
private WorldConfiguration.Misc getConfig() {
return ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle().paperConfig().misc;
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, @StaticValue("alternate_current") String __, WorldConfiguration.Misc.AlternateCurrentUpdateOrder updateOrder) {
WorldConfiguration.Misc misc = getConfig();
misc.redstoneImplementation = WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT;
misc.alternateCurrentUpdateOrder = updateOrder;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, WorldConfiguration.Misc.RedstoneImplementation implementation) {
getConfig().redstoneImplementation = implementation;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
WorldConfiguration.Misc misc = getConfig();
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
broadcastTitle(List.of(event.getPlayer()));
}
}
private void broadcastTitle(Collection<? extends Player> players) {
WorldConfiguration.Misc misc = getConfig();
Component title = switch (misc.redstoneImplementation) {
case VANILLA -> Component.text("").color(NamedTextColor.RED).append(Component.text(" Redstone: Vanilla ").color(NamedTextColor.WHITE)).append(Component.text("").color(NamedTextColor.RED));
case EIGENCRAFT -> Component.text("Redstone: Eigencraft");
case ALTERNATE_CURRENT -> Component.text("").color(NamedTextColor.RED).append(Component.text(" Redstone: AC ").color(NamedTextColor.WHITE)).append(Component.text("").color(NamedTextColor.RED));
};
Component subtitle;
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
subtitle = Component.text().build();
} else {
subtitle = switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD -> Component.text("Y first inwards"); // Y before XZ
case VERTICAL_FIRST_OUTWARD -> Component.text("Y first outwards"); // XZ before Y
case HORIZONTAL_FIRST_INWARD -> Component.text("XZ first inwards"); // Y before XZ
case HORIZONTAL_FIRST_OUTWARD -> Component.text("XZ first outwards"); // XZ before Y
};
}
players.forEach(player -> {
player.sendTitlePart(TitlePart.TITLE, title);
player.sendTitlePart(TitlePart.SUBTITLE, subtitle);
});
}
@Override
public ScoreboardGroup getGroup() {
return ScoreboardGroup.FOOTER;
}
@Override
public int order() {
return Integer.MAX_VALUE;
}
@Override
public String get(Region region, Player p) {
WorldConfiguration.Misc misc = getConfig();
switch (misc.redstoneImplementation) {
case ALTERNATE_CURRENT:
switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7Y in§8)";
case VERTICAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7Y out§8)";
case HORIZONTAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7XZ in§8)";
case HORIZONTAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7XZ out§8)";
}
return "§eRedstone§8: §cAC";
case EIGENCRAFT:
return null;
case VANILLA:
default:
return "§eRedstone§8: §cVanilla";
}
}
}
@@ -24,9 +24,9 @@ import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
import de.steamwar.bausystem.utils.bossbar.BossBarService;
import de.steamwar.entity.RBlockDisplay;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
@@ -290,8 +290,8 @@ public class KillcheckerVisualizer {
}
rEntities.get(point).die();
}
RFallingBlockEntity entity = new RFallingBlockEntity(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5), MATERIALS[Math.min(count - 1, MATERIALS.length) - 1]);
entity.setNoGravity(true);
RBlockDisplay entity = new RBlockDisplay(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5));
entity.setBlock(MATERIALS[Math.min(count - 1, MATERIALS.length) - 1].createBlockData());
rEntities.put(point, entity);
if (outlinePoints.contains(point)) outlinePointsCache.add(point);
killCount.put(point, count);
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.features.simulator;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
@@ -38,77 +37,80 @@ import de.steamwar.bausystem.features.simulator.gui.SimulatorGui;
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.bausystem.utils.ItemUtils;
import de.steamwar.bausystem.utils.RayTraceUtils;
import de.steamwar.core.SWPlayer;
import de.steamwar.cursor.Cursor;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.linkage.Linked;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
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.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.player.*;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
@Linked
public class SimulatorCursor implements Listener {
private static final World WORLD = Bukkit.getWorlds().get(0);
private static Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static Map<Player, REntityServer> cursors = Collections.synchronizedMap(new HashMap<>());
private static final Set<Player> calculating = new HashSet<>();
private static final Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static final Map<Player, REntityServer> emptyTargetServers = Collections.synchronizedMap(new HashMap<>());
public static boolean isSimulatorItem(ItemStack itemStack) {
return ItemUtils.isItem(itemStack, "simulator");
}
public SimulatorCursor() {
BiFunction<Player, Object, Object> function = (player, object) -> {
calcCursor(player);
return object;
};
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, function);
private static boolean hasSimulatorItem(Player player) {
return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand());
}
private static void scheduleCursorUpdate(Player player) {
BauSystem.runTaskLater(BauSystem.getInstance(), () -> calcCursor(player), 1);
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
calcCursor(event.getPlayer());
}, 0);
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
calcCursor(event.getPlayer());
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onPlayerItemHeld(PlayerItemHeldEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
calcCursor(event.getPlayer());
}, 1);
scheduleCursorUpdate(event.getPlayer());
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
}
@EventHandler
@@ -119,10 +121,7 @@ public class SimulatorCursor implements Listener {
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
cursorType.remove(event.getPlayer());
cursors.remove(event.getPlayer());
synchronized (calculating) {
calculating.remove(event.getPlayer());
}
removeCursor(event.getPlayer());
}
private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>();
@@ -138,7 +137,7 @@ public class SimulatorCursor implements Listener {
public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return;
Player player = event.getPlayer();
if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) {
if (!hasSimulatorItem(player)) {
return;
}
if (LAST_SNEAKS.containsKey(player)) {
@@ -164,17 +163,10 @@ public class SimulatorCursor implements Listener {
}
public static void calcCursor(Player player) {
synchronized (calculating) {
if (calculating.contains(player)) return;
calculating.add(player);
}
if (!Permission.BUILD.hasPermission(player) || (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand()))) {
if (removeCursor(player) || SimulatorWatcher.show(null, player)) {
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) {
if (removeCursor(player) | SimulatorWatcher.show(null, player)) {
SWUtils.sendToActionbar(player, "");
}
synchronized (calculating) {
calculating.remove(player);
}
return;
}
@@ -183,203 +175,98 @@ public class SimulatorCursor implements Listener {
removeCursor(player);
SimulatorWatcher.show(null, player);
SWUtils.sendToActionbar(player, "§cGenerating Stab");
synchronized (calculating) {
calculating.remove(player);
}
return;
}
SimulatorWatcher.show(simulator, player);
List<REntity> entities = SimulatorWatcher.getEntitiesOfSimulator(simulator);
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), entities);
if (rayTraceResult == null) {
removeCursor(player);
if (simulator == null) {
SWUtils.sendToActionbar(player, "§eSelect Simulator");
} else {
SWUtils.sendToActionbar(player, "§eOpen Simulator");
}
synchronized (calculating) {
calculating.remove(player);
}
return;
Cursor cursor = getOrCreateCursor(player, simulator, cursorType.getOrDefault(player, CursorType.TNT));
cursor.renderDeduplicated();
}
private static Cursor getOrCreateCursor(Player player, Simulator simulator, CursorType type) {
REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator);
SWPlayer swPlayer = SWPlayer.of(player);
Optional<Cursor> activeCursor = swPlayer.getComponent(Cursor.class);
Cursor cursor = activeCursor.orElse(null);
if (cursor == null || cursor.getTargetServer() != targetServer) {
swPlayer.removeComponent(Cursor.class);
cursor = new Cursor(
targetServer,
player,
Material.GLASS,
type.material,
type.cursorModes,
(location, hitEntity, action) -> handlePlayerClick(player, location, hitEntity, action),
(location, hitEntity) -> sendCursorActionbar(player, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent())
);
} else {
cursor.setCursorMaterial(type.material);
cursor.setAllowedCursorModes(type.cursorModes);
}
showCursor(player, rayTraceResult, simulator != null);
synchronized (calculating) {
calculating.remove(player);
}
return cursor;
}
private static synchronized boolean removeCursor(Player player) {
REntityServer entityServer = cursors.get(player);
boolean hadCursor = entityServer != null && !entityServer.getEntities().isEmpty();
if (entityServer != null) {
entityServer.getEntities().forEach(REntity::die);
Optional<Cursor> cursor = SWPlayer.of(player).getComponent(Cursor.class);
cursor.ifPresent(__ -> SWPlayer.of(player).removeComponent(Cursor.class));
REntityServer emptyTargetServer = emptyTargetServers.remove(player);
if (emptyTargetServer != null) {
emptyTargetServer.close();
}
return hadCursor;
return cursor.isPresent();
}
private static synchronized void showCursor(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, boolean hasSimulatorSelected) {
REntityServer entityServer = cursors.computeIfAbsent(player, __ -> {
REntityServer rEntityServer = new REntityServer();
rEntityServer.addPlayer(player);
return rEntityServer;
});
CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
REntity hitEntity = rayTraceResult.getHitEntity();
Location location = hitEntity != null ? new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD) :
type.position.apply(player, rayTraceResult).toLocation(WORLD);
Material material = hitEntity != null ? Material.GLASS : type.getMaterial();
List<RFallingBlockEntity> entities = entityServer.getEntitiesByType(RFallingBlockEntity.class);
entities.removeIf(rFallingBlockEntity -> {
if (rFallingBlockEntity.getMaterial() != material) {
rFallingBlockEntity.die();
return true;
}
rFallingBlockEntity.move(location);
return false;
});
if (entities.isEmpty()) {
RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, location, material);
rFallingBlockEntity.setNoGravity(true);
if (material == Material.GLASS) {
rFallingBlockEntity.setGlowing(true);
}
}
if (hasSimulatorSelected) {
if (hitEntity != null) {
SWUtils.sendToActionbar(player, "§eEdit Position");
} else {
SWUtils.sendToActionbar(player, "§eAdd new " + type.name);
}
} else {
private static void sendCursorActionbar(Player player, Simulator simulator, boolean hasCursorLocation, boolean hasHitEntity) {
if (!hasCursorLocation) {
SWUtils.sendToActionbar(player, simulator == null ? "§eSelect Simulator" : "§eOpen Simulator");
} else if (simulator == null) {
SWUtils.sendToActionbar(player, "§eCreate new Simulator");
}
}
public static Vector getPosFree(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
if (face.getModY() == 0 && player.isSneaking()) {
pos.setY(pos.getY() - 0.49);
}
}
if (!player.isSneaking()) {
pos.setX(pos.getBlockX() + 0.5);
if (face == null || face.getModY() == 0) {
pos.setY(pos.getBlockY() + 0.0);
}
pos.setZ(pos.getBlockZ() + 0.5);
}
return pos;
}
private static Vector getPosBlockAligned(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
}
pos.setX(pos.getBlockX() + 0.5);
if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) {
pos.setY(pos.getBlockY() + 1.0);
} else if (hasHitEntity) {
SWUtils.sendToActionbar(player, "§eEdit Position");
} else {
pos.setY(pos.getBlockY());
SWUtils.sendToActionbar(player, "§eAdd new " + cursorType.getOrDefault(player, CursorType.TNT).name);
}
pos.setZ(pos.getBlockZ() + 0.5);
return pos;
}
@Getter
@AllArgsConstructor
public enum CursorType {
TNT(Material.TNT, Material.GUNPOWDER, SimulatorCursor::getPosFree, "TNT", vector -> new TNTElement(vector).add(new TNTPhase())),
REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, SimulatorCursor::getPosBlockAligned, "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())),
OBSERVER(Material.OBSERVER, Material.QUARTZ, SimulatorCursor::getPosBlockAligned, "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())),
TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE, Cursor.CursorMode.SURFACE_ALIGNED), "TNT", vector -> new TNTElement(vector).add(new TNTPhase())),
REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())),
OBSERVER(Material.OBSERVER, Material.QUARTZ, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())),
;
public final Material material;
public final Material nonSelectedMaterial;
public final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> position;
public final List<Cursor.CursorMode> cursorModes;
public final String name;
public final Function<Vector, SimulatorElement<?>> elementFunction;
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
if (!ItemUtils.isItem(event.getItem(), "simulator")) {
private static void handlePlayerClick(Player player, Location cursorLocation, Optional<REntity> hitEntity, Action action) {
if (!Permission.BUILD.hasPermission(player)) return;
if (!hasSimulatorItem(player)) {
return;
}
event.setCancelled(true);
Player player = event.getPlayer();
Simulator simulator = SimulatorStorage.getSimulator(player);
if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_AIR) {
if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) {
if (simulator == null) {
return;
}
SimulatorExecutor.run(event.getPlayer(), simulator, null);
SimulatorExecutor.run(player, simulator, null);
return;
}
if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) {
if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) {
return;
}
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator));
if (simulator == null) {
if (rayTraceResult == null) {
if (cursorLocation == null) {
SimulatorStorage.openSimulatorSelector(player);
} else {
SWAnvilInv anvilInv = new SWAnvilInv(player, "Name");
@@ -395,7 +282,7 @@ public class SimulatorCursor implements Listener {
}
sim = new Simulator(s);
SimulatorStorage.addSimulator(s, sim);
createElement(player, rayTraceResult, sim);
createElement(player, cursorLocation, sim);
SimulatorStorage.setSimulator(player, sim);
});
anvilInv.open();
@@ -403,57 +290,56 @@ public class SimulatorCursor implements Listener {
return;
}
if (rayTraceResult == null) {
if (cursorLocation == null) {
new SimulatorGui(player, simulator).open();
return;
}
if (rayTraceResult.getHitEntity() != null) {
REntity hitEntity = rayTraceResult.getHitEntity();
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<SimulatorElement<?>> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> {
return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0);
}).collect(Collectors.toList());
switch (elements.size()) {
case 0:
return;
case 1:
// Open single element present in Simulator
SimulatorElement<?> element = elements.get(0);
SimulatorGroup group1 = element.getGroup(simulator);
SimulatorBaseGui back = new SimulatorGui(player, simulator);
if (group1.getElements().size() > 1) {
back = new SimulatorGroupGui(player, simulator, group1, back);
}
element.open(player, simulator, group1, back);
break;
default:
List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList());
if (parents.size() == 1) {
// Open multi element present in Simulator in existing group
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open();
} else {
// Open multi element present in Simulator in implicit group
SimulatorGroup group2 = new SimulatorGroup();
group2.setMaterial(null);
group2.getElements().addAll(elements);
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, group2, simulatorGui).open();
}
break;
}
if (hitEntity.isPresent()) {
openElement(player, simulator, hitEntity.get());
return;
}
// Add new Element to current simulator
createElement(player, rayTraceResult, simulator);
createElement(player, cursorLocation, simulator);
}
private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) {
private static void openElement(Player player, Simulator simulator, REntity hitEntity) {
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<SimulatorElement<?>> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> {
return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0);
}).collect(Collectors.toList());
switch (elements.size()) {
case 0:
return;
case 1:
SimulatorElement<?> element = elements.get(0);
SimulatorGroup group1 = element.getGroup(simulator);
SimulatorBaseGui back = new SimulatorGui(player, simulator);
if (group1.getElements().size() > 1) {
back = new SimulatorGroupGui(player, simulator, group1, back);
}
element.open(player, simulator, group1, back);
break;
default:
List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList());
if (parents.size() == 1) {
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open();
} else {
SimulatorGroup group2 = new SimulatorGroup();
group2.setMaterial(null);
group2.getElements().addAll(elements);
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, group2, simulatorGui).open();
}
break;
}
}
private static void createElement(Player player, Location cursorLocation, Simulator simulator) {
CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
Vector vector = type.position.apply(player, rayTraceResult);
Vector vector = cursorLocation.toVector();
if (type == CursorType.REDSTONE_BLOCK) {
vector.subtract(new Vector(0.5, 0, 0.5));
}
@@ -124,4 +124,11 @@ public class SimulatorWatcher {
}
return entityServer.getEntities();
}
synchronized REntityServer getEntityServerOfSimulator(Simulator simulator) {
if (simulator == null) {
return null;
}
return entityServers.computeIfAbsent(simulator, __ -> createSim(new REntityServer(), simulator));
}
}
@@ -0,0 +1,72 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.slaves.blastresistance;
import com.google.common.util.concurrent.AtomicDouble;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.block.BlockState;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.concurrent.atomic.AtomicInteger;
@Linked
public class BlastResistanceCommand extends SWCommand {
public BlastResistanceCommand() {
super("blastresistance");
}
@Register(description = "BLASTRESISTANCE_HELP")
public void command(@Validator Player player) {
LocalSession localSession = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player));
Clipboard clipboard;
try {
clipboard = localSession.getClipboard().getClipboards().getFirst();
} catch (WorldEditException e) {
BauSystem.MESSAGE.send("BLASTRESISTANCE_NO_CLIPBOARD", player);
return;
}
AtomicDouble min = new AtomicDouble(0);
AtomicDouble max = new AtomicDouble(0);
AtomicDouble sum = new AtomicDouble(0);
AtomicInteger count = new AtomicInteger(0);
clipboard.forEach(blockVector3 -> {
BlockState blockState = clipboard.getBlock(blockVector3);
Material material = BukkitAdapter.adapt(blockState).getMaterial();
if (material == Material.WATER || material == Material.LAVA) return;
double blastResistance = BukkitAdapter.adapt(blockState).getMaterial().getBlastResistance();
min.set(Math.min(min.get(), blastResistance));
max.set(Math.max(max.get(), blastResistance));
sum.addAndGet(blastResistance);
count.getAndIncrement();
});
BauSystem.MESSAGE.send("BLASTRESISTANCE_RESULT", player, min.get(), max.get(), sum.get() / count.get());
}
}
@@ -21,7 +21,6 @@ package de.steamwar.bausystem.features.slaves.laufbau;
import com.sk89q.worldedit.blocks.SkullBlock;
import com.sk89q.worldedit.world.block.BaseBlock;
import de.steamwar.bausystem.utils.NMSWrapper;
import de.steamwar.inventory.SWItem;
import lombok.Getter;
import lombok.ToString;
@@ -99,7 +98,7 @@ public class BlockBoundingBox {
// addPixel(Material.COBWEB.createBlockData(), 0, 0, 0, 0, 0, 0, createItem("LAUFBAU_BLOCK_COBWEB", Material.COBWEB));
addPixel(Material.END_STONE.createBlockData(), 0, 0, 0, 16, 16, 16, null);
addPixel(NMSWrapper.impl.pathMaterial().createBlockData(), 0, 0, 0, 16, 15, 16, createItem("LAUFBAU_BLOCK_GRASS_PATH", NMSWrapper.impl.pathMaterial()));
addPixel(Material.DIRT_PATH.createBlockData(), 0, 0, 0, 16, 15, 16, createItem("LAUFBAU_BLOCK_GRASS_PATH", Material.DIRT_PATH));
addPixel(Material.MUD.createBlockData(), 0, 0, 0, 16, 14, 16, createItem("LAUFBAU_BLOCK_SOUL_SAND", Material.SOUL_SAND));
Cocoa cocoaNorth = (Cocoa) Material.COCOA.createBlockData();
@@ -31,7 +31,7 @@ import de.steamwar.command.TypeMapper;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.TechHider;
import de.steamwar.techhider.legacy.TechHider;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
@@ -19,13 +19,13 @@
package de.steamwar.bausystem.features.tpslimit;
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.bausystem.utils.TickEndEvent;
import de.steamwar.bausystem.utils.TickManager;
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
import de.steamwar.bausystem.utils.bossbar.BossBarService;
@@ -72,7 +72,7 @@ public class TPSSystem implements Listener {
}
@EventHandler
public void onTickEnd(TickEndEvent event) {
public void onTickEnd(ServerTickEndEvent event) {
bossbar();
}
@@ -143,14 +143,6 @@ public class Trace {
} else {
entityServer = new REntityServer();
entityServer.addPlayer(player);
entityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) {
return;
}
if (rEntity instanceof TraceEntity) {
((TraceEntity) rEntity).printIntoChat(p);
}
});
entityServerMap.put(player, entityServer);
}
render(getRecords(), entityServer, playerTraceShowData);
@@ -167,14 +159,6 @@ public class Trace {
REntityServer entityServer = entityServerMap.computeIfAbsent(player, k -> {
REntityServer newEntityServer = new REntityServer();
newEntityServer.addPlayer(k);
newEntityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) {
return;
}
if (rEntity instanceof TraceEntity) {
((TraceEntity) rEntity).printIntoChat(p);
}
});
return newEntityServer;
});
@@ -24,13 +24,18 @@ import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.bausystem.features.tracer.Trace;
import de.steamwar.bausystem.features.tracer.TraceManager;
import de.steamwar.entity.RBlockDisplay;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.entity.RInteraction;
import lombok.Getter;
import net.md_5.bungee.api.chat.ClickEvent;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.util.Transformation;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import yapion.hierarchy.types.YAPIONValue;
import java.util.List;
@@ -41,7 +46,16 @@ import static de.steamwar.bausystem.features.util.TNTClickListener.TNT_CLICK_DET
/**
* Wrapper for the rendering of a record bundle
*/
public class TraceEntity extends RFallingBlockEntity {
public class TraceEntity extends RBlockDisplay {
private static final float TNT_VISUAL_SCALE = 0.98F;
private static final float TNT_VISUAL_OFFSET = -TNT_VISUAL_SCALE / 2.0F;
private static final Transformation TNT_VISUAL_TRANSFORM = new Transformation(
new Vector3f(TNT_VISUAL_OFFSET, 0.0F, TNT_VISUAL_OFFSET),
new Quaternionf(0, 0, 0, 1),
new Vector3f(TNT_VISUAL_SCALE, TNT_VISUAL_SCALE, TNT_VISUAL_SCALE),
new Quaternionf(0, 0, 0, 1)
);
/**
* The records represented by this REntity
@@ -55,13 +69,31 @@ public class TraceEntity extends RFallingBlockEntity {
private final String uniqueTntIdsString;
private final Trace trace;
private final RInteraction hitbox;
public TraceEntity(REntityServer server, Location location, boolean isExplosion, List<TNTPoint> records, Trace trace) {
super(server, location, isExplosion ? Material.RED_STAINED_GLASS : Material.TNT);
super(server, location);
Material material = isExplosion ? Material.RED_STAINED_GLASS : Material.TNT;
this.records = records;
this.trace = trace;
uniqueTntIdsString = records.stream().map(TNTPoint::getTntId).distinct().map(Object::toString).collect(Collectors.joining(" "));
setNoGravity(true);
hitbox = new RInteraction(server, location);
hitbox.setInteractionHeight(TNT_VISUAL_SCALE);
hitbox.setInteractionWidth(TNT_VISUAL_SCALE);
hitbox.setCallback((player, action) -> {
if (action == REntityAction.INTERACT) {
printIntoChat(player);
}
});
setTransform(TNT_VISUAL_TRANSFORM);
setBlock(material.createBlockData());
}
@Override
public void die() {
hitbox.die();
super.die();
}
/**
@@ -20,8 +20,8 @@
package de.steamwar.bausystem.features.tracer.rendering;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.entity.RBlockDisplay;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
@@ -129,8 +129,8 @@ public abstract class ViewFlag {
Location yLocation = previous.getLocation().clone().add(0, delta.getY(), 0);
if (yLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && yLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
RFallingBlockEntity y = new RFallingBlockEntity(server, yLocation, Material.WHITE_STAINED_GLASS);
y.setNoGravity(true);
RBlockDisplay y = new RBlockDisplay(server, yLocation);
y.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
}
Location secoundLocation;
@@ -141,8 +141,8 @@ public abstract class ViewFlag {
}
if (secoundLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && secoundLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
RFallingBlockEntity second = new RFallingBlockEntity(server, secoundLocation, Material.WHITE_STAINED_GLASS);
second.setNoGravity(true);
RBlockDisplay second = new RBlockDisplay(server, secoundLocation);
second.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
}
}
}
@@ -38,6 +38,7 @@ import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -47,7 +48,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -73,19 +73,7 @@ public class BindCommand extends SWCommand implements Listener {
}
}
private static final CommandMap commandMap;
static {
Field knownCommandsField;
try {
knownCommandsField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
knownCommandsField.setAccessible(true);
commandMap = (CommandMap) knownCommandsField.get(Bukkit.getServer());
} catch (IllegalAccessException | NoSuchFieldException var2) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", var2);
}
}
private static final CommandMap commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap();
private static final NamespacedKey KEY = SWUtils.getNamespaceKey("command");
@@ -24,14 +24,17 @@ import com.mojang.authlib.GameProfile;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.bausystem.utils.NMSWrapper;
import de.steamwar.command.SWCommand;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.core.SWPlayer;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.*;
import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -57,11 +60,11 @@ public class NoClipCommand extends SWCommand implements Listener {
public NoClipCommand() {
super("noclip", "nc");
BiFunction<Player, Object, Object> first = (player, o) -> {
BiFunction<Player, ServerboundMovePlayerPacket, Object> first = (player, o) -> {
NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null);
if (noClipData == null) return o;
if (noClipData.lastTick == TPSUtils.currentTick.get()) return o;
NMSWrapper.impl.setInternalGameMode(player, GameMode.SPECTATOR);
setInternalGameMode(player, GameMode.SPECTATOR);
noClipData.lastTick = TPSUtils.currentTick.get();
return o;
};
@@ -71,7 +74,7 @@ public class NoClipCommand extends SWCommand implements Listener {
BiFunction<Player, Object, Object> second = (player, o) -> {
NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null);
if (noClipData == null) return o;
NMSWrapper.impl.setInternalGameMode(player, GameMode.CREATIVE);
setInternalGameMode(player, GameMode.CREATIVE);
noClipData.lastTick = TPSUtils.currentTick.get();
return o;
};
@@ -79,15 +82,29 @@ public class NoClipCommand extends SWCommand implements Listener {
TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, second);
TinyProtocol.instance.addFilter(ServerboundContainerClickPacket.class, second);
BiFunction<Player, Object, Object> third = (player, o) -> {
BiFunction<Player, ServerboundSetCreativeModeSlotPacket, Object> third = (player, o) -> {
if (SWPlayer.of(player).hasComponent(NoClipData.class)) {
NMSWrapper.impl.setSlotToItemStack(player, o);
int index = o.slotNum();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(o.itemStack()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
return o;
};
TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third);
}
private void setInternalGameMode(Player player, GameMode gameMode) {
((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue());
}
@Register(help = true)
public void genericCommand(@Validator Player player) {
SWPlayer swPlayer = SWPlayer.of(player);
@@ -95,7 +112,9 @@ public class NoClipCommand extends SWCommand implements Listener {
swPlayer.removeComponent(NoClipData.class);
} else {
player.setGameMode(GameMode.SPECTATOR);
NMSWrapper.impl.setPlayerBuildAbilities(player);
Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
abilities.mayBuild = true;
abilities.mayfly = true;
swPlayer.setComponent(new NoClipData());
BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player);
@@ -28,6 +28,7 @@ import de.steamwar.bausystem.utils.bossbar.BossBarService;
import de.steamwar.linkage.Linked;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
import io.papermc.paper.event.player.PlayerPickBlockEvent;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
@@ -127,4 +128,11 @@ public class BauMemberUpdate extends PacketHandler implements Listener {
}
}, 1);
}
@EventHandler
public void onPlayerClick(PlayerPickBlockEvent event) {
if(SPECTATORS.contains(event.getPlayer())) {
event.setCancelled(true);
}
}
}
@@ -19,8 +19,9 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.utils.NMSWrapper;
import de.steamwar.linkage.Linked;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemContainerContents;
import org.bukkit.attribute.Attribute;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
@@ -32,6 +33,8 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.List;
@Linked
public class InventoryListener implements Listener {
@@ -57,7 +60,7 @@ public class InventoryListener implements Listener {
}
stack.setItemMeta(meta);
}
if (NMSWrapper.impl.checkItemStack(stack)) {
if (checkItemStack(stack)) {
e.setCurrentItem(null);
e.setCancelled(true);
return;
@@ -73,7 +76,7 @@ public class InventoryListener implements Listener {
for (int i = 0; i < content.length; i++) {
if (content[i] == null) continue;
int finalI = i;
if (NMSWrapper.impl.checkItemStack(content[finalI])) {
if (checkItemStack(content[finalI])) {
p.getInventory().setItem(i, null);
}
}
@@ -82,11 +85,44 @@ public class InventoryListener implements Listener {
@EventHandler(ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent event) {
Player p = event.getPlayer();
if (NMSWrapper.impl.checkItemStack(event.getItemInHand())) {
if (checkItemStack(event.getItemInHand())) {
event.setCancelled(true);
event.setBuild(false);
p.getInventory().setItemInMainHand(null);
p.getInventory().setItemInOffHand(null);
}
}
private static final int threshold = 2048;
private boolean checkItemStack(ItemStack item) {
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
return false;
}
return drillDown(data.contents(), 0, 0) > threshold;
}
private int drillDown(List<ItemStack> items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (int i = start; i < items.size(); i++) {
ItemStack item = items.get(i);
if (item.isEmpty()) continue;
invalid += item.getAmount();
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
continue;
}
List<ItemStack> subItems = data.contents();
if (subItems.size() > 1) {
invalid = drillDown(subItems, layer + 1, invalid);
}
}
return invalid;
}
}
@@ -20,18 +20,24 @@
package de.steamwar.bausystem.features.world;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.utils.NMSWrapper;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import org.bukkit.GameMode;
import java.util.Optional;
@Linked
public class NoCreativeKnockback {
public NoCreativeKnockback() {
TinyProtocol.instance.addFilter(ClientboundExplodePacket.class, (player, o) -> {
if (player.getGameMode() != GameMode.CREATIVE) return o;
return NMSWrapper.impl.resetExplosionKnockback(o);
return new ClientboundExplodePacket(
o.center(),
Optional.empty(),
o.explosionParticle(),
o.explosionSound()
);
});
}
}
@@ -101,7 +101,7 @@ public class SignEdit implements Listener {
}
{
TinyProtocol.instance.addTypedFilter(ServerboundSignUpdatePacket.class, (player, o) -> {
TinyProtocol.instance.addFilter(ServerboundSignUpdatePacket.class, (player, o) -> {
Bukkit.getScheduler().runTask(BauSystem.getInstance(), () -> {
ServerLevel serverLevel = ((CraftWorld) player.getWorld()).getHandle();
Block signLoc = CraftBlock.at(serverLevel, o.getPos());
@@ -25,7 +25,7 @@ import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.BauweltMember;
import de.steamwar.techhider.TechHider;
import de.steamwar.techhider.legacy.TechHider;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -57,7 +57,7 @@ public class WorldEditListener implements Listener {
private static final Set<String> commands = new HashSet<>();
private static final Set<String> commandExclusions = new HashSet<>();
private static final String[] shortcutCommands = {"//1", "//2", "//90", "//-90", "//180", "//p", "//c", "//flopy", "//floppy", "//flopyp", "//floppyp", "//u", "//r"};
private static final String[] shortcutCommands = {"//1", "//2", "//90", "//-90", "//180", "//p", "//c", "//flopy", "//floppy", "//flopyp", "//floppyp", "//u", "//r", "//download", "/download"};
public static boolean isWorldEditCommand(String command) {
for (String shortcut : shortcutCommands) {
@@ -28,7 +28,7 @@ import de.steamwar.command.SWCommand;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.TechHider;
import de.steamwar.techhider.legacy.TechHider;
import net.md_5.bungee.api.ChatMessageType;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
@@ -115,8 +115,8 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
return packet;
};
TinyProtocol.instance.addTypedFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter);
TinyProtocol.instance.addTypedFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter);
}
@EventHandler
@@ -20,28 +20,30 @@
package de.steamwar.bausystem.utils;
import de.steamwar.bausystem.SWUtils;
import lombok.experimental.UtilityClass;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@UtilityClass
public class ItemUtils {
public final class ItemUtils {
private final NamespacedKey ITEM_KEY = SWUtils.getNamespaceKey("bau_item");
private static final NamespacedKey ITEM_KEY = SWUtils.getNamespaceKey("bau_item");
public boolean isItem(ItemStack itemStack, String tag) {
private ItemUtils() {
}
public static boolean isItem(ItemStack itemStack, String tag) {
String value = getTag(itemStack, ITEM_KEY);
return value != null && value.equals(tag);
}
public void setItem(ItemStack itemStack, String tag) {
public static ItemStack setItem(ItemStack itemStack, String tag) {
setTag(itemStack, ITEM_KEY, tag);
return itemStack;
}
public String getTag(ItemStack itemStack, NamespacedKey key) {
public static String getTag(ItemStack itemStack, NamespacedKey key) {
if (itemStack == null) {
return null;
}
@@ -56,7 +58,7 @@ public class ItemUtils {
return container.get(key, PersistentDataType.STRING);
}
public void setTag(ItemStack itemStack, NamespacedKey key, String value) {
public static void setTag(ItemStack itemStack, NamespacedKey key, String value) {
if (itemStack == null) {
return;
}
@@ -1,117 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.utils;
import de.steamwar.Reflection;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemContainerContents;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional;
public class NMSWrapper {
public static final NMSWrapper impl = new NMSWrapper();
private static final Reflection.Field<GameType> playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0);
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue()));
}
public void setSlotToItemStack(Player player, Object o) {
ClientboundContainerSetSlotPacket packetPlayInSetCreativeSlot = (ClientboundContainerSetSlotPacket) o;
int index = packetPlayInSetCreativeSlot.getSlot();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItem()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
public void setPlayerBuildAbilities(Player player) {
Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
abilities.mayBuild = true;
abilities.mayfly = true;
}
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
public boolean checkItemStack(ItemStack item) {
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
return false;
}
return drillDown(data.contents(), 0, 0) > threshold;
}
private int drillDown(List<ItemStack> items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (int i = start; i < items.size(); i++) {
ItemStack item = items.get(i);
if (item.isEmpty()) continue;
invalid += item.getAmount();
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
continue;
}
List<ItemStack> subItems = data.contents();
if (subItems.size() > 1) {
invalid = drillDown(subItems, layer + 1, invalid);
}
}
return invalid;
}
public Object resetExplosionKnockback(Object packet) {
ClientboundExplodePacket explosion = (ClientboundExplodePacket) packet;
return new ClientboundExplodePacket(
explosion.center(),
Optional.empty(),
explosion.explosionParticle(),
explosion.explosionSound()
);
}
}
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.utils;
import de.steamwar.Reflection;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.UtilityClass;
@@ -86,9 +85,6 @@ public class PlaceItemUtils {
.collect(Collectors.toSet());
}
private static final Reflection.Field<?> positionAccessor = Reflection.getField(CraftBlockState.class, BlockPos.class, 0);
private static final Reflection.Field<?> worldAccessor = Reflection.getField(CraftBlockState.class, CraftWorld.class, 0);
/**
* Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World.
* This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a
@@ -288,8 +284,9 @@ public class PlaceItemUtils {
} else {
// If a BlockState is present set the Position and World to the Block you want to place
Location blockLocation = block.getLocation();
positionAccessor.set(blockState, new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()));
worldAccessor.set(blockState, blockLocation.getWorld());
CraftBlockState craftBlockState = (CraftBlockState) blockState;
craftBlockState.position = new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ());
craftBlockState.world = (CraftWorld) blockLocation.getWorld();
}
if (blockData.getMaterial().isSolid()) {
@@ -20,7 +20,6 @@
package de.steamwar.bausystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import net.minecraft.network.protocol.game.ClientboundTickingStatePacket;
import net.minecraft.server.MinecraftServer;
@@ -33,7 +32,6 @@ public class TickManager implements Listener {
public static final TickManager impl = new TickManager();
private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager();
private static final Reflection.Field<Long> remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0);
private boolean blockTpsPacket = true;
private int totalSteps;
@@ -42,7 +40,7 @@ public class TickManager implements Listener {
TinyProtocol.instance.addFilter(ClientboundTickingStatePacket.class, this::blockPacket);
}
private Object blockPacket(Player player, Object packet) {
private Object blockPacket(Player player, ClientboundTickingStatePacket packet) {
if (blockTpsPacket) {
return new ClientboundTickingStatePacket(20, manager.isFrozen());
} else {
@@ -121,7 +119,7 @@ public class TickManager implements Listener {
public long getRemainingTicks() {
if (isSprinting()) {
return remainingSprintTicks.get(manager);
return manager.remainingSprintTicks;
} else {
return manager.frozenTicksToRun();
}
@@ -19,6 +19,7 @@
package de.steamwar.bausystem.utils;
import com.fastasyncworldedit.core.regions.selector.PolyhedralRegionSelector;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
@@ -32,16 +33,19 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.*;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import de.steamwar.Reflection;
import com.sk89q.worldedit.world.World;
import de.steamwar.bausystem.shared.Pair;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@UtilityClass
@@ -91,17 +95,36 @@ public class WorldEditUtils {
.getRegionSelector(BukkitAdapter.adapt(player.getWorld()));
return new Pair<>(regionSelector.getClass(), regionSelector.getVertices()
.stream()
.map(blockVector3 -> blockVector3 == null ? null : adapt(player.getWorld(), blockVector3))
.map(blockVector3 -> {
if (blockVector3 == null) {
return null;
} else {
return BukkitAdapter.adapt(player.getWorld(), blockVector3);
}
})
.collect(Collectors.toList()));
}
private static final Map<Class<? extends RegionSelector>, Function<World, RegionSelector>> constructors = new HashMap<>();
static {
constructors.put(CuboidRegionSelector.class, CuboidRegionSelector::new);
constructors.put(ExtendingCuboidRegionSelector.class, ExtendingCuboidRegionSelector::new);
constructors.put(Polygonal2DRegionSelector.class, Polygonal2DRegionSelector::new);
constructors.put(EllipsoidRegionSelector.class, EllipsoidRegionSelector::new);
constructors.put(SphereRegionSelector.class, SphereRegionSelector::new);
constructors.put(CylinderRegionSelector.class, CylinderRegionSelector::new);
constructors.put(ConvexPolyhedralRegionSelector.class, ConvexPolyhedralRegionSelector::new);
constructors.put(PolyhedralRegionSelector.class, PolyhedralRegionSelector::new);
}
public void setVertices(Player player, Class<? extends RegionSelector> clazz, List<Location> vertices) {
LocalSession localSession = WorldEdit.getInstance()
.getSessionManager()
.get(BukkitAdapter.adapt(player));
Reflection.Constructor constructorInvoker = Reflection.getConstructor(clazz, com.sk89q.worldedit.world.World.class);
RegionSelector regionSelector = (RegionSelector) constructorInvoker.invoke(BukkitAdapter.adapt(player.getWorld()));
Function<World, RegionSelector> constructor = constructors.get(clazz);
if (constructor == null) return;
RegionSelector regionSelector = constructor.apply(BukkitAdapter.adapt(player.getWorld()));
localSession.setRegionSelector(BukkitAdapter.adapt(player.getWorld()), regionSelector);
if (vertices.isEmpty()) return;
@@ -127,13 +150,9 @@ public class WorldEditUtils {
try {
BlockVector3 min = regionSelector.getRegion().getMinimumPoint();
BlockVector3 max = regionSelector.getRegion().getMaximumPoint();
return new Pair<>(adapt(player.getWorld(), min), adapt(player.getWorld(), max));
return new Pair<>(BukkitAdapter.adapt(player.getWorld(), min), BukkitAdapter.adapt(player.getWorld(), max));
} catch (IncompleteRegionException e) {
return null;
}
}
private Location adapt(World world, BlockVector3 blockVector3) {
return new Location(world, blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ version: "2.0"
depend: [ WorldEdit, SpigotCore ]
load: POSTWORLD
main: de.steamwar.bausystem.BauSystem
api-version: "1.13"
api-version: "1.21"
website: "https://steamwar.de"
description: "So unseriös wie wir sind: BauSystem nur besser."
+1 -9
View File
@@ -31,15 +31,6 @@ dependencies {
implementation(project(":BauSystem:BauSystem_Main"))
}
tasks.register<DevServer>("DevBau20") {
group = "run"
description = "Run a 1.20 Dev Bau"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar")
dependsOn(":SchematicSystem:shadowJar")
template = "Bau20"
}
tasks.register<DevServer>("DevBau21") {
group = "run"
description = "Run a 1.21 Dev Bau"
@@ -47,4 +38,5 @@ tasks.register<DevServer>("DevBau21") {
dependsOn(":BauSystem:shadowJar")
dependsOn(":SchematicSystem:shadowJar")
template = "Bau21"
debug = true
}
+1
View File
@@ -18,6 +18,7 @@ dependencies {
implementation("com.github.ajalt.clikt:clikt:5.0.3")
implementation("com.github.ajalt.mordant:mordant:3.0.2")
implementation(libs.logback)
implementation("org.yaml:snakeyaml:2.2")
implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1")
implementation(libs.exposedCore)
+63 -11
View File
@@ -14,9 +14,11 @@ import com.github.ajalt.clikt.parameters.types.file
import com.github.ajalt.clikt.parameters.types.long
import com.github.ajalt.clikt.parameters.types.path
import com.sun.security.auth.module.UnixSystem
import org.yaml.snakeyaml.Yaml
import java.io.File
import kotlin.io.path.absolute
import kotlin.io.path.absolutePathString
import kotlin.random.Random
const val LOG4J_CONFIG = """<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.mojang.util">
@@ -69,32 +71,42 @@ class DevCommand : CliktCommand("dev") {
override fun run() {
val args = mutableListOf<String>()
val serverDirectory = File(workingDir, server)
val serverDir =
if (serverDirectory.exists() && serverDirectory.isDirectory) serverDirectory else File(workingDir, server)
var serverDir = resolveServerDirectory(server)
if (isVelocity(server)) {
runServer(
args, jvmArgs, listOf(
jar?.absolutePath
?: File("/jar/Velocity.jar").absolutePath
?: File("/jars/Velocity.jar").absolutePath
), serverDir
)
} else {
setLogConfig(args)
val version = findVersion(server)
?: throw CliktError("Unknown Server Version")
val gameModeTemplate = if (serverDir.isDirectory) null else loadGameModeTemplate(server)
if (gameModeTemplate != null) {
serverDir = gameModeTemplate.serverDir
args += "-Dconfig=$server.yml"
}
val worldFile = world?.absolute()?.toFile()
?: File(serverDir, "devtempworld")
val jarFile = jar?.absolutePath
?: File(workingDir, "devtempworld")
var jarFile = jar?.absolutePath
?: additionalVersions[server]?.let { supportedVersionJars[it] }
?: supportedVersionJars[version]
?: throw CliktError("Unknown Server Version")
if (gameModeTemplate != null) {
jarFile = if (gameModeTemplate.spigot) {
jarFile.replace("paper", "spigot")
} else {
jarFile.replace("spigot", "paper")
}
}
if (!worldFile.exists()) {
val templateFile = File(serverDir, "Bauwelt")
val templateFile = gameModeTemplate?.worldTemplate ?: File(serverDir, "Bauwelt")
if (!templateFile.exists()) {
throw CliktError("World Template not found!")
throw CliktError("Could not find world template: ${templateFile.absolutePath}")
}
templateFile.copyRecursively(worldFile)
}
@@ -123,6 +135,12 @@ class DevCommand : CliktCommand("dev") {
}
}
data class GameModeTemplate(
val serverDir: File,
val worldTemplate: File,
val spigot: Boolean
)
val jvmDefaultParams = arrayOf(
"-Xmx1G",
"-Xgc:excessiveGCratio=80",
@@ -152,8 +170,7 @@ class DevCommand : CliktCommand("dev") {
)
val additionalVersions = mapOf(
"Tutorial" to 15,
"Lobby" to 20
"Lobby" to 21
)
fun findVersion(server: String): Int? =
@@ -166,6 +183,41 @@ class DevCommand : CliktCommand("dev") {
fun isVelocity(server: String): Boolean =
server.endsWith("Velocity")
fun resolveServerDirectory(server: String): File {
val localServer = File(workingDir, server)
if (localServer.isDirectory) {
return localServer
}
return File("/servers", server)
}
fun loadGameModeTemplate(server: String): GameModeTemplate? {
val configFile = File("/configs/GameModes/$server.yml")
if (!configFile.exists()) {
throw CliktError("Server/GameMode not found")
}
val document = configFile.reader().use { reader ->
Yaml().load<Map<String, Any?>>(reader)
} ?: throw CliktError("GameMode config is empty: ${configFile.absolutePath}")
val serverConfig = document["Server"] as? Map<*, *>
?: throw CliktError("GameMode config is missing Server section: ${configFile.absolutePath}")
val folder = serverConfig["Folder"] as? String
?: throw CliktError("GameMode config is missing Server.Folder: ${configFile.absolutePath}")
val maps = (serverConfig["Maps"] as? List<*>)
?.filterIsInstance<String>()
?.takeIf { it.isNotEmpty() }
?: throw CliktError("GameMode config is missing Server.Maps: ${configFile.absolutePath}")
val serverDir = File("/servers", folder)
val worldTemplate = File(File(serverDir, "arenas"), maps[Random.nextInt(maps.size)])
return GameModeTemplate(
serverDir = serverDir,
worldTemplate = worldTemplate,
spigot = serverConfig["Spigot"] == true
)
}
fun setLogConfig(args: MutableList<String>) {
args += "-DlogPath=${workingDir.absolutePath}/logs"
args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}"
@@ -191,4 +243,4 @@ class DevCommand : CliktCommand("dev") {
Runtime.getRuntime().addShutdownHook(Thread { if (process.isAlive) process.destroyForcibly() })
process.waitFor()
}
}
}
@@ -434,7 +434,7 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
private var nodeItem by SchematicNodeTable.item
var item: String
get() = nodeItem.ifEmpty {
if (isDir()) "CHEST" else "CAULDRON_ITEM"
if (isDir()) "CHEST" else "CAULDRON"
}
set(value) = useDb {
nodeItem = value
@@ -25,6 +25,7 @@ import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.inList
import org.jetbrains.exposed.v1.core.neq
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.insert
@@ -136,6 +137,12 @@ class SteamwarUser(id: EntityID<Int>) : IntEntity(id) {
.select(SteamwarUserTable.fields).where { UserPermTable.perm eq perm }.map { wrapRow(it) }
}
@JvmStatic
fun getUsersWithDiscordId() =
useDb {
find { SteamwarUserTable.discordId neq null }.toList()
}
@JvmStatic
fun getServerTeam() =
useDb {
@@ -37,4 +37,5 @@ dependencies {
compileOnly(libs.authlib)
compileOnly(libs.nms)
compileOnly(libs.fawe)
compileOnly(libs.datafixer)
}
@@ -32,7 +32,10 @@ import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.utils.FightUI;
import de.steamwar.fightsystem.utils.HullHider;
import de.steamwar.fightsystem.utils.SWSound;
import de.steamwar.fightsystem.utils.TechHiderWrapper;
import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message;
@@ -41,6 +44,9 @@ import de.steamwar.sql.SchematicNode;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerPickupArrowEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class FightSystem extends JavaPlugin {
@@ -95,13 +101,20 @@ public class FightSystem extends JavaPlugin {
getMessage().broadcast("PISTON_PUSHED_OUTSIDE");
shutdown();
});
new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
new StateDependentListener(ArenaMode.All, FightState.All, new Listener() {
@EventHandler
public void onArrowPickup(PlayerPickupArrowEvent e) {
if (Fight.fighting(e.getPlayer())) e.setCancelled(true);
}
});
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
techHider = new TechHiderWrapper();
hullHider = new HullHider();
techHider = new TechHiderWrapper(hullHider);
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
FileSource.startReplay();
@@ -120,7 +133,7 @@ public class FightSystem extends JavaPlugin {
Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID));
}
CraftbukkitWrapper.impl.setupGamerule();
Config.world.setGameRule(GameRule.LOCATOR_BAR, false);
}
@Override
@@ -21,8 +21,8 @@ import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.fightsystem.utils.TpsWarper;
import de.steamwar.linkage.Linked;
import net.minecraft.server.MinecraftServer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -45,8 +45,7 @@ public class TPSWarpCommand implements CommandExecutor {
return false;
}
TpsWarper warper = TpsWarper.impl;
warper.warp(tps);
MinecraftServer.getServer().tickRateManager().setTickRate(tps);
FightSystem.getMessage().broadcastActionbar("TPSWARP_SET", tps);
return false;
@@ -27,12 +27,9 @@ import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.utils.Message;
import de.steamwar.fightsystem.utils.Region;
import de.steamwar.fightsystem.utils.SWSound;
import de.steamwar.techhider.ProtocolUtils;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import java.util.List;
public class EnternCountdown extends Countdown {
private static int calcTime(FightPlayer fp, Countdown countdown) {
@@ -47,7 +44,6 @@ public class EnternCountdown extends Countdown {
}
private final FightPlayer fightPlayer;
private List<ProtocolUtils.ChunkPos> chunkPos;
public EnternCountdown(FightPlayer fp, Countdown countdown) {
super(calcTime(fp, countdown), new Message("ENTERN_COUNTDOWN"), SWSound.BLOCK_NOTE_PLING, false);
@@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.record.GlobalRecorder;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.entity.LivingEntity;
@@ -88,7 +89,7 @@ public class Fight {
}
public static void playSound(Sound sound, float volume, float pitch) {
GlobalRecorder.getInstance().soundAtPlayer(sound.name(), volume, pitch);
GlobalRecorder.getInstance().soundAtPlayer(Registry.SOUNDS.getKey(sound).getKey(), volume, pitch);
//volume: max. 100, pitch: max. 2
Bukkit.getServer().getOnlinePlayers().forEach(player -> player.playSound(player, sound, volume, pitch));
}
@@ -158,6 +158,8 @@ public class FightSchematic extends StateDependent {
FreezeWorld freezer = new FreezeWorld();
team.teleportToSpawn();
// TODO: Implement hull generation based on clipboard content!
FightSystem.getHullHider().fill(team, false);
Vector dims = WorldeditWrapper.impl.getDimensions(clipboard);
WorldeditWrapper.impl.pasteClipboard(
clipboard,
@@ -35,7 +35,9 @@ import de.steamwar.fightsystem.listener.TeamArea;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.utils.FightUI;
import de.steamwar.fightsystem.utils.ItemBuilder;
import de.steamwar.fightsystem.utils.Region;
import de.steamwar.fightsystem.winconditions.Wincondition;
import de.steamwar.fightsystem.winconditions.Winconditions;
import de.steamwar.inventory.SWItem;
@@ -44,6 +46,8 @@ import de.steamwar.sql.SteamwarUser;
import lombok.Getter;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.NameTagVisibility;
@@ -151,8 +155,8 @@ public class FightTeam {
new TeamArea(this);
team = FightScoreboard.getBukkitTeam(name);
WorldOfColorWrapper.impl.setTeamColor(team, color);
BountifulWrapper.impl.setNametagVisibility(team);
team.setColor(color);
team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM);
team.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OTHER_TEAMS);
if (!Config.GameModeConfig.WinConditions.contains(Winconditions.AMONG_US)) {
team.setAllowFriendlyFire(false);
@@ -284,7 +288,8 @@ public class FightTeam {
entity.teleport(spawn);
fightPlayer.ifPlayer(player -> {
BountifulWrapper.impl.setAttackSpeed(player);
AttributeInstance attribute = player.getAttribute(Attribute.ATTACK_SPEED);
attribute.setBaseValue(16);
player.setFoodLevel(20);
player.getInventory().clear();
FightSystem.getHullHider().updatePlayer(player);
@@ -26,7 +26,6 @@ import de.steamwar.fightsystem.listener.Recording;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.linkage.Linked;
import lombok.Getter;
import org.bukkit.Bukkit;
@@ -64,11 +63,18 @@ public class FightWorld extends StateDependent {
public static void forceLoad() {
Config.ArenaRegion.forEachChunk((cX, cZ) -> {
Config.world.loadChunk(cX, cZ);
FlatteningWrapper.impl.forceLoadChunk(Config.world, cX, cZ);
Config.world.setChunkForceLoaded(cX, cZ, true);
});
}
public static void resetWorld() {
World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld();
assert backup != null;
Config.ArenaRegion.forEachChunk((x, z) -> {
CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z);
});
Bukkit.unloadWorld(backup, false);
List<Entity> entities = new ArrayList<>();
Recording.iterateOverEntities(Objects::nonNull, entity -> {
if (entity.getType() != EntityType.PLAYER && (!Config.GameModeConfig.Arena.Leaveable || Config.ArenaRegion.inRegion(entity.getLocation()))) {
@@ -78,14 +84,12 @@ public class FightWorld extends StateDependent {
entities.forEach(Entity::remove);
entities.clear();
World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld();
assert backup != null;
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
Config.ArenaRegion.forEachChunk((x, z) -> {
CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z);
for (Player p : Bukkit.getOnlinePlayers()) {
de.steamwar.core.CraftbukkitWrapper.sendChunk(p, x, z);
}
});
Bukkit.unloadWorld(backup, false);
}
}
@@ -20,7 +20,6 @@
package de.steamwar.fightsystem.fight;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.utils.BountifulWrapper;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
@@ -30,19 +29,16 @@ import org.bukkit.event.block.*;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
public class FreezeWorld implements Listener {
private final Listener denyHandSwap = BountifulWrapper.impl.newDenyHandSwapListener();
public FreezeWorld() {
Bukkit.getPluginManager().registerEvents(this, FightSystem.getPlugin());
Bukkit.getPluginManager().registerEvents(denyHandSwap, FightSystem.getPlugin());
}
public void disable() {
HandlerList.unregisterAll(this);
HandlerList.unregisterAll(denyHandSwap);
}
@EventHandler
@@ -94,4 +90,9 @@ public class FreezeWorld implements Listener {
public void handlePlayerInteract(PlayerInteractEvent event) {
event.setCancelled(true);
}
@EventHandler
public void onSwapItems(PlayerSwapHandItemsEvent event) {
if (Fight.fighting(event.getPlayer())) event.setCancelled(true);
}
}
@@ -24,12 +24,12 @@ import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.commands.Commands;
import de.steamwar.fightsystem.commands.GUI;
import de.steamwar.fightsystem.listener.PersonalKitCreator;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.ReflectionWrapper;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.sql.PersonalKit;
import de.steamwar.sql.SteamwarUser;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.datacomponent.DataComponentTypes;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
@@ -39,6 +39,7 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockDataMeta;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
@@ -193,13 +194,13 @@ public class Kit {
if (Config.GameModeConfig.Kits.ForbiddenItems.contains(stack.getType())) return true;
//Check for attribute modifiers
if (FlatteningWrapper.impl.hasAttributeModifier(stack)) {
if (stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers()) {
return true;
}
if (stack.hasItemMeta()) {
ItemMeta meta = stack.getItemMeta();
if (FlatteningWrapper.impl.containsBlockMeta(meta)) return true; //Blocks always upwards slabs etc.
if (meta instanceof BlockDataMeta && ((BlockDataMeta) meta).hasBlockData()) return true; //Blocks always upwards slabs etc.
if (hasItems(stack)) return true; //Blocks prefilled inventories
}
@@ -208,8 +209,42 @@ public class Kit {
return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty();
}
private static final Set<DataComponentType> FORBIDDEN_TYPES = new HashSet<>();
static {
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_NAME);
FORBIDDEN_TYPES.add(DataComponentTypes.PROFILE);
FORBIDDEN_TYPES.add(DataComponentTypes.UNBREAKABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCK_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS);
FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS);
FORBIDDEN_TYPES.add(DataComponentTypes.TOOL);
FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON);
FORBIDDEN_TYPES.add(DataComponentTypes.FOOD);
FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON);
FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN);
FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES);
FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION);
FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST);
FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE);
}
public static boolean hasItems(ItemStack stack) {
return ReflectionWrapper.impl.hasItems(stack);
FORBIDDEN_TYPES.forEach(stack::resetData);
return false;
}
private boolean isEnchantmentInKit(ItemStack stack) {
@@ -22,12 +22,12 @@ package de.steamwar.fightsystem.listener;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.utils.WorldOfColorWrapper;
import de.steamwar.linkage.Linked;
import net.minecraft.world.entity.projectile.AbstractArrow;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.projectiles.ProjectileSource;
@@ -46,6 +46,7 @@ public class ArrowStopper {
private void run() {
Recording.iterateOverEntities(AbstractArrow.class::isInstance, entity -> {
Projectile arrow = (Projectile) entity;
if(!(arrow.getShooter() instanceof Player)) return;
if (invalidEntity(arrow)) return;
Location prevLocation = arrow.getLocation().toVector().subtract(arrow.getVelocity()).toLocation(arrow.getWorld());
@@ -93,8 +94,12 @@ public class ArrowStopper {
boolean teamFrom = entity.getVelocity().getZ() > 0;
boolean overMid = location.getZ() > Config.SpecSpawn.getZ();
boolean otherSide = teamFrom == overMid;
return otherSide || !Config.ArenaRegion.inRegion(location) ||
WorldOfColorWrapper.impl.isInBlock(entity) ||
if (otherSide || !Config.ArenaRegion.inRegion(location)) return true;
boolean result = false;
if (entity instanceof Arrow arrow) {
result = arrow.isInBlock();
}
return result ||
entity.getVelocity().equals(NULL_VECTOR);
}
}
@@ -22,11 +22,11 @@ package de.steamwar.fightsystem.listener;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.linkage.Linked;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
@@ -46,7 +46,7 @@ public class BlockPlaceCollision implements Listener {
// Hitbox size: 0.6xz, 1.8y, 1.5y when sneaking
Player player = event.getPlayer();
Location min = player.getLocation().add(-0.3, 0, -0.3);
Location max = player.getLocation().add(0.3, FlatteningWrapper.impl.isCrouching(player) ? 0.6 : (player.isSneaking() ? 1.5 : 1.8), 0.3);
Location max = player.getLocation().add(0.3, player.getPose() == Pose.SWIMMING ? 0.6 : (player.isSneaking() ? 1.5 : 1.8), 0.3);
Location blockmin = block.getLocation();
Location blockmax = block.getLocation().add(1.0, 1.0, 1.0);
@@ -24,7 +24,6 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.Region;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
@@ -141,7 +140,7 @@ public class Border {
private void sendChange(Player player, Block block, Material type) {
if (block.getType() == Material.AIR) {
FlatteningWrapper.impl.sendBlockChange(player, block, type);
player.sendBlockChange(block.getLocation(), type.createBlockData());
}
}
}
@@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import org.bukkit.entity.Player;
import java.io.*;
@@ -42,7 +43,7 @@ public class ClickAnalyzer {
}
public ClickAnalyzer() {
TinyProtocol.instance.addFilter(Recording.blockPlacePacket, this::onBlockPlace);
TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, this::onBlockPlace);
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, this::onBlockPlace);
}
@@ -20,24 +20,22 @@
package de.steamwar.fightsystem.listener;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.BountifulWrapper;
import de.steamwar.linkage.Linked;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
@Linked
public class DenyInventoryMovement implements Listener {
public DenyInventoryMovement() {
new StateDependentListener(ArenaMode.AntiTest, FightState.AntiIngame, this);
Listener listener = BountifulWrapper.impl.newDenyHandSwapListener();
new StateDependentListener(ArenaMode.AntiTest, FightState.AntiIngame, listener);
}
@EventHandler
@@ -54,4 +52,9 @@ public class DenyInventoryMovement implements Listener {
public void onItemPickup(PlayerPickupItemEvent event) {
event.setCancelled(true);
}
@EventHandler
public void onSwapItems(PlayerSwapHandItemsEvent event) {
if (Fight.fighting(event.getPlayer())) event.setCancelled(true);
}
}
@@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
@@ -38,11 +39,13 @@ public class EntityDamage implements Listener {
@EventHandler
public void handleEntityDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof Player)) return;
if (Config.ArenaRegion.in2dRegion(event.getEntity().getLocation())) event.setCancelled(true);
}
@EventHandler
public void handleEntityDamageByEntity(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof Player)) return;
if (Config.ArenaRegion.in2dRegion(event.getEntity().getLocation())) event.setCancelled(true);
}
}
@@ -25,7 +25,6 @@ import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
@@ -27,13 +27,12 @@ import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.BountifulWrapper;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.linkage.Linked;
import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.type.Dispenser;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.EventHandler;
@@ -62,7 +61,7 @@ public class Permanent implements Listener {
private static final Team spectatorTeam = FightScoreboard.getBukkitTeam("Spectator");
static {
BountifulWrapper.impl.setNametagVisibility(spectatorTeam);
spectatorTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM);
spectatorTeam.setNameTagVisibility(NameTagVisibility.NEVER);
}
@@ -234,7 +233,7 @@ public class Permanent implements Listener {
return;
}
if (e.getItem().getType() == Material.TNT || FlatteningWrapper.impl.isFacingWater(block)) {
if (e.getItem().getType() == Material.TNT || block.getRelative(((Dispenser) block.getBlockData()).getFacing()).isLiquid()) {
e.setCancelled(true);
}
}
@@ -1,74 +0,0 @@
package de.steamwar.fightsystem.listener;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@Linked
public class PlayerJoinListener implements Listener {
public PlayerJoinListener() {
new StateDependentListener(true, FightState.All, this);
}
@EventHandler()
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
World world = player.getWorld();
Location loc = player.getLocation();
int viewDistance = Bukkit.getViewDistance();
int chunkX = loc.getChunk().getX();
int chunkZ = loc.getChunk().getZ();
// Iterate through the chunks around the player and force a resend
for (int x = -viewDistance; x <= viewDistance; x++) {
for (int z = -viewDistance; z <= viewDistance; z++) {
Chunk chunk = world.getChunkAt(chunkX + x, chunkZ + z);
this.forceRefreshChunkForPlayer(player, chunk);
}
}
}
public void forceRefreshChunkForPlayer(Player player, Chunk bukkitChunk) {
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
CraftWorld craftWorld = (CraftWorld) bukkitChunk.getWorld();
int chunkX = bukkitChunk.getX();
int chunkZ = bukkitChunk.getZ();
LevelChunk nmsChunk = craftWorld.getHandle().getChunkSource().getChunk(chunkX, chunkZ, false);
if (nmsChunk == null) {
// Chunk isn't loaded in memory on the server side;
return;
}
ClientboundForgetLevelChunkPacket unloadPacket = new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ));
serverPlayer.connection.send(unloadPacket);
ClientboundLevelChunkWithLightPacket loadPacket = new ClientboundLevelChunkWithLightPacket(
nmsChunk,
craftWorld.getHandle().getLightEngine(),
null,
null,
true
);
serverPlayer.connection.send(loadPacket);
}
}
@@ -28,12 +28,12 @@ import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.Region;
import de.steamwar.fightsystem.utils.WorldeditWrapper;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.SchematicNode;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -52,7 +52,7 @@ public class PrepareSchem implements Listener {
new OneShotStateDependent(ArenaMode.Prepare, FightState.PostSchemSetup, () -> Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> {
stationaryMovingPistons.clear();
Fight.getUnrotated().getSchemRegion().forEach((x, y, z) -> {
if (FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z))) {
if (Config.world.getBlockAt(x, y, z).getType() == Material.MOVING_PISTON) {
stationaryMovingPistons.add(new Vector(x, y, z));
}
});
@@ -76,7 +76,7 @@ public class PrepareSchem implements Listener {
try {
region.forEach((x, y, z) -> {
if (FlatteningWrapper.impl.checkPistonMoving(Config.world.getBlockAt(x, y, z)) && !stationaryMovingPistons.contains(new Vector(x, y, z))) {
if (Config.world.getBlockAt(x, y, z).getType() == Material.MOVING_PISTON && !stationaryMovingPistons.contains(new Vector(x, y, z))) {
FightSystem.getMessage().broadcast("PREPARE_ACTIVE_PISTON");
Bukkit.shutdown();
throw new IllegalStateException();
@@ -20,8 +20,8 @@
package de.steamwar.fightsystem.listener;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.events.TeamDeathEvent;
import de.steamwar.fightsystem.events.TeamLeaveEvent;
@@ -34,17 +34,16 @@ import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.utils.BountifulWrapper;
import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.SWSound;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.item.PrimedTnt;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
@@ -63,6 +62,7 @@ import java.util.Random;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.StreamSupport;
@Linked
public class Recording implements Listener {
@@ -83,12 +83,22 @@ public class Recording implements Listener {
public static final Class<?> primedTnt = PrimedTnt.class;
public static void iterateOverEntities(Predicate<Object> filter, Consumer<Entity> consumer) {
CraftbukkitWrapper.impl.entityIterator().filter(filter).map(net.minecraft.world.entity.Entity::getBukkitEntity).forEach(consumer);
StreamSupport.stream(((CraftWorld) Config.world).getHandle().getEntities().getAll().spliterator(), false).filter(filter).map(net.minecraft.world.entity.Entity::getBukkitEntity).forEach(consumer);
}
public Recording() {
new StateDependentListener(ArenaMode.AntiReplay, FightState.All, this);
new StateDependentListener(ArenaMode.AntiReplay, FightState.All, BountifulWrapper.impl.newHandSwapRecorder());
new StateDependentListener(ArenaMode.AntiReplay, FightState.All, new Listener() {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onItemSwap(PlayerSwapHandItemsEvent e) {
if (isNotSent(e.getPlayer()))
return;
Player player = e.getPlayer();
GlobalRecorder.getInstance().item(player, disarmNull(e.getMainHandItem()), "MAINHAND");
GlobalRecorder.getInstance().item(player, disarmNull(e.getOffHandItem()), "OFFHAND");
}
});
new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) {
@Override
public void enable() {
@@ -102,19 +112,19 @@ public class Recording implements Listener {
}
}.register();
new StateDependent(ArenaMode.AntiReplay, FightState.Ingame) {
private final BiFunction<Player, Object, Object> place = Recording.this::blockPlace;
private final BiFunction<Player, Object, Object> dig = Recording.this::blockDig;
private final BiFunction<Player, ServerboundUseItemPacket, Object> place = Recording.this::blockPlace;
private final BiFunction<Player, ServerboundPlayerActionPacket, Object> dig = Recording.this::blockDig;
@Override
public void enable() {
TinyProtocol.instance.addFilter(blockPlacePacket, place);
TinyProtocol.instance.addFilter(blockDigPacket, dig);
TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, place);
TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, dig);
}
@Override
public void disable() {
TinyProtocol.instance.removeFilter(blockPlacePacket, place);
TinyProtocol.instance.removeFilter(blockDigPacket, dig);
TinyProtocol.instance.removeFilter(ServerboundUseItemPacket.class, place);
TinyProtocol.instance.removeFilter(ServerboundPlayerActionPacket.class, dig);
}
}.register();
new StateDependentTask(ArenaMode.AntiReplay, FightState.All, () -> {
@@ -131,23 +141,16 @@ public class Recording implements Listener {
GlobalRecorder.getInstance().entitySpeed(entity);
}
private static final Class<?> blockDigPacket = ServerboundPlayerActionPacket.class;
private static final Class<?> playerDigType = blockDigPacket.getDeclaredClasses()[0];
private static final Reflection.Field<?> blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0);
private static final Object releaseUseItem = playerDigType.getEnumConstants()[5];
private Object blockDig(Player p, Object packet) {
if (!isNotSent(p) && blockDigType.get(packet) == releaseUseItem) {
private Object blockDig(Player p, ServerboundPlayerActionPacket packet) {
if (!isNotSent(p) && packet.getAction() == ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM) {
GlobalRecorder.getInstance().bowSpan(p, false, false);
}
return packet;
}
public static final Class<?> blockPlacePacket = ServerboundUseItemPacket.class;
private Object blockPlace(Player p, Object packet) {
boolean mainHand = BountifulWrapper.impl.mainHand(packet);
if (!isNotSent(p) && BountifulWrapper.impl.bowInHand(mainHand, p)) {
private Object blockPlace(Player p, ServerboundUseItemPacket packet) {
boolean mainHand = packet.getHand() == InteractionHand.MAIN_HAND;
if (!isNotSent(p) && (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW) {
GlobalRecorder.getInstance().bowSpan(p, true, !mainHand);
}
return packet;
@@ -177,7 +180,7 @@ public class Recording implements Listener {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onBlockPhysics(BlockPhysicsEvent e) {
if (FlatteningWrapper.impl.doRecord(e)) {
if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) {
GlobalRecorder.getInstance().blockChange(e.getBlock());
}
}
@@ -285,7 +288,8 @@ public class Recording implements Listener {
if (!fp.isLiving()) continue;
fp.ifPlayer(player -> {
BountifulWrapper.impl.recordHandItems(player);
GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getItemInMainHand()), "MAINHAND");
GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getItemInOffHand()), "OFFHAND");
GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getHelmet()), "HEAD");
GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getChestplate()), "CHEST");
GlobalRecorder.getInstance().item(player, disarmNull(player.getInventory().getLeggings()), "LEGS");
@@ -25,11 +25,13 @@ import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.linkage.Linked;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -101,7 +103,25 @@ public class WaterRemover implements Listener {
if (!Config.BlueExtendRegion.inRegion(b) && !Config.RedExtendRegion.inRegion(b)) return;
//checks for water and removes it, if present
if (!FlatteningWrapper.impl.removeWater(b)) return;
boolean result = true;
Material type = b.getType();
if (type == Material.WATER || type == Material.LAVA) {
b.setType(Material.AIR);
} else {
BlockData data = b.getBlockData();
if (!(data instanceof Waterlogged)) {
result = false;
} else {
Waterlogged waterlogged = (Waterlogged) data;
if (waterlogged.isWaterlogged()) {
b.setType(Material.AIR);
} else {
result = false;
}
}
}
if (!result) return;
if (b.getY() < MIN_Y) return;
@@ -34,7 +34,9 @@ import de.steamwar.fightsystem.fight.FightWorld;
import de.steamwar.fightsystem.fight.FreezeWorld;
import de.steamwar.fightsystem.listener.FightScoreboard;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.utils.FightUI;
import de.steamwar.fightsystem.utils.Message;
import de.steamwar.fightsystem.utils.TechHiderWrapper;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.Team;
@@ -42,11 +44,14 @@ import de.steamwar.techhider.BlockIds;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Pose;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.*;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@@ -72,7 +77,7 @@ public class PacketProcessor implements Listener {
private static final org.bukkit.scoreboard.Team team = FightScoreboard.getBukkitTeam("Replay");
static {
BountifulWrapper.impl.setNametagVisibility(team);
team.setOption(org.bukkit.scoreboard.Team.Option.NAME_TAG_VISIBILITY, org.bukkit.scoreboard.Team.OptionStatus.FOR_OWN_TEAM);
team.setNameTagVisibility(NameTagVisibility.NEVER);
}
@@ -460,8 +465,15 @@ public class PacketProcessor implements Listener {
if (!Config.ArenaRegion.in2dRegion(x, z)) return; //Outside of the arena
execSync(() -> {
BlockIdWrapper.impl.setBlock(Config.world, x, y, z, TechHiderWrapper.ENABLED && hiddenBlockIds.contains(blockState) ? obfuscateWith : blockState);
FightSystem.getHullHider().blockUpdate(Config.world.getBlockAt(x, y, z), BlockIdWrapper.impl.idToMaterial(blockState));
int blockState1 = TechHiderWrapper.ENABLED && hiddenBlockIds.contains(blockState) ? obfuscateWith : blockState;
BlockState blockData = Block.stateById(blockState1);
ServerLevel level = ((CraftWorld) Config.world).getHandle();
BlockPos pos = new BlockPos(x, y, z);
level.removeBlockEntity(pos);
level.setBlock(pos, blockData, blockState1);
level.getChunkSource().blockChanged(pos);
FightSystem.getHullHider().blockUpdate(Config.world.getBlockAt(x, y, z), CraftMagicNumbers.getMaterial(Block.stateById(blockState)).getItemType());
});
}
@@ -478,7 +490,7 @@ public class PacketProcessor implements Listener {
double finalX = x;
double finalZ = z;
execSync(() -> BountifulWrapper.impl.spawnParticle(Config.world, particleName, finalX + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), finalZ + Config.ArenaRegion.getMinZ()));
execSync(() -> Config.world.spawnParticle(Particle.valueOf(particleName), finalX + Config.ArenaRegion.getMinX(), y + Config.BluePasteRegion.getMinY(), finalZ + Config.ArenaRegion.getMinZ(), 1));
}
private void sound() throws IOException {
@@ -500,9 +512,14 @@ public class PacketProcessor implements Listener {
float volume = source.readFloat();
float pitch = source.readFloat();
Sound sound = Sound.valueOf(soundName);
Sound sound = Registry.SOUNDS.get(NamespacedKey.minecraft(soundName));
if (sound == null) sound = Sound.valueOf(soundName); // TODO: Remove in 26.x because of no longer needed backwards compatibility
Sound finalSound = sound;
execSync(() -> WorldOfColorWrapper.impl.playSound(new Location(Config.world, x, y, z), sound, soundCategory, volume, pitch));
execSync(() -> {
Location location = new Location(Config.world, x, y, z);
location.getWorld().playSound(location, finalSound, SoundCategory.valueOf(soundCategory), volume, pitch);
});
}
private void soundAtPlayer() throws IOException {
@@ -511,9 +528,11 @@ public class PacketProcessor implements Listener {
float volume = source.readFloat();
float pitch = source.readFloat();
Sound sound = Sound.valueOf(soundName);
Sound sound = Registry.SOUNDS.get(NamespacedKey.minecraft(soundName));
if (sound == null) sound = Sound.valueOf(soundName); // TODO: Remove in 26.x because of no longer needed backwards compatibility
Sound finalSound = sound;
execSync(() -> Fight.playSound(sound, volume, pitch));
execSync(() -> Fight.playSound(finalSound, volume, pitch));
}
private void pasteSchem(FightTeam team) throws IOException {
@@ -603,7 +622,7 @@ public class PacketProcessor implements Listener {
Bukkit.getOnlinePlayers().forEach(p -> {
p.resetTitle();
WorldOfColorWrapper.impl.sendTitle(p, title, subtitle, 5, 40, 5);
p.sendTitle(title, subtitle, 5, 40, 5);
});
}
@@ -25,7 +25,6 @@ import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.utils.BlockIdWrapper;
import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
import de.steamwar.fightsystem.utils.Message;
import de.steamwar.fightsystem.utils.SWSound;
@@ -34,7 +33,9 @@ import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Registry;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@@ -219,7 +220,7 @@ public interface Recorder {
}
default void blockChange(Block block) {
int blockState = BlockIdWrapper.impl.blockToId(block);
int blockState = net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle());
int shortX = block.getX() - Config.ArenaRegion.getMinX();
int shortY = block.getY() - Config.BluePasteRegion.getMinY();
@@ -238,7 +239,7 @@ public interface Recorder {
}
default void sound(int x, int y, int z, SWSound soundType, String soundCategory, float volume, float pitch) {
write(0x32, x, y, z, soundType.getSound().name(), soundCategory, volume, pitch);
write(0x32, x, y, z, Registry.SOUNDS.getKey(soundType.getSound()).getKey(), soundCategory, volume, pitch);
}
default void soundAtPlayer(String soundType, float volume, float pitch) {
@@ -1,75 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.fightsystem.FightSystem;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
public class BlockIdWrapper {
public static final BlockIdWrapper impl = new BlockIdWrapper();
public Material idToMaterial(int blockState) {
return CraftMagicNumbers.getMaterial(net.minecraft.world.level.block.Block.stateById(blockState)).getItemType();
}
public int blockToId(Block block) {
return net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle());
}
public void setBlock(World world, int x, int y, int z, int blockState) {
BlockState blockData = net.minecraft.world.level.block.Block.stateById(blockState);
ServerLevel level = ((CraftWorld) world).getHandle();
BlockPos pos = new BlockPos(x, y, z);
level.removeBlockEntity(pos);
level.setBlock(pos, blockData, blockState);
level.getChunkSource().blockChanged(pos);
}
public void trackEntity(Player player, Entity entity) {
if (entity instanceof Player) {
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
}
player.showEntity(FightSystem.getPlugin(), entity);
}
public void untrackEntity(Player player, Entity entity) {
player.hideEntity(FightSystem.getPlugin(), entity);
if (entity instanceof Player) {
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
}
}
}
@@ -1,158 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import de.steamwar.Reflection;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.Recording;
import de.steamwar.fightsystem.record.GlobalRecorder;
import net.minecraft.world.InteractionHand;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
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.PlayerPickupArrowEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
import org.bukkit.scoreboard.Team;
import java.util.HashMap;
import java.util.Map;
public class BountifulWrapper {
public static final BountifulWrapper impl = new BountifulWrapper();
private static final Class<?> enumHand = InteractionHand.class;
private static final Object mainHand = enumHand.getEnumConstants()[0];
private static final Reflection.Field<?> blockPlaceHand = Reflection.getField(Recording.blockPlacePacket, enumHand, 0);
public boolean mainHand(Object packet) {
return blockPlaceHand.get(packet) == mainHand;
}
public boolean bowInHand(boolean mainHand, Player p) {
return (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW;
}
public void setAttackSpeed(Player player) {
AttributeInstance attribute = player.getAttribute(Attribute.ATTACK_SPEED);
attribute.setBaseValue(16);
}
public void setNametagVisibility(Team team) {
team.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.FOR_OWN_TEAM);
}
public Listener newDenyArrowPickupListener() {
return new Listener() {
@EventHandler
public void onArrowPickup(PlayerPickupArrowEvent e) {
if (Fight.fighting(e.getPlayer())) e.setCancelled(true);
}
};
}
public Listener newDenyHandSwapListener() {
return new Listener() {
@EventHandler
public void onSwapItems(PlayerSwapHandItemsEvent event) {
if (Fight.fighting(event.getPlayer())) event.setCancelled(true);
}
};
}
public void recordHandItems(Player player) {
GlobalRecorder.getInstance().item(player, Recording.disarmNull(player.getInventory().getItemInMainHand()), "MAINHAND");
GlobalRecorder.getInstance().item(player, Recording.disarmNull(player.getInventory().getItemInOffHand()), "OFFHAND");
}
public Listener newHandSwapRecorder() {
return new Listener() {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onItemSwap(PlayerSwapHandItemsEvent e) {
if (Recording.isNotSent(e.getPlayer()))
return;
Player player = e.getPlayer();
GlobalRecorder.getInstance().item(player, Recording.disarmNull(e.getMainHandItem()), "MAINHAND");
GlobalRecorder.getInstance().item(player, Recording.disarmNull(e.getOffHandItem()), "OFFHAND");
}
};
}
public void spawnParticle(World world, String particleName, double x, double y, double z) {
world.spawnParticle(Particle.valueOf(particleName), x, y, z, 1);
}
private final Map<Player, BossBar> barMap = new HashMap<>();
public void sendBar(Player player, FightTeam team, double progress, String text) {
barMap.keySet().removeIf(p -> !p.isOnline());
if (!barMap.containsKey(player)) {
BossBar bar = Bukkit.createBossBar(player.getName(), BarColor.WHITE, BarStyle.SOLID);
barMap.put(player, bar);
bar.addPlayer(player);
}
BossBar bar = barMap.get(player);
BarColor color = chat2bar(team.getColor());
if (bar.getColor() != color) bar.setColor(color);
if (bar.getProgress() != progress) bar.setProgress(progress);
if (!bar.getTitle().equals(text)) bar.setTitle(text);
}
private BarColor chat2bar(ChatColor color) {
switch (color) {
case DARK_BLUE:
case DARK_AQUA:
case BLUE:
case AQUA:
return BarColor.BLUE;
case GREEN:
case DARK_GREEN:
return BarColor.GREEN;
case DARK_RED:
case RED:
return BarColor.RED;
case DARK_PURPLE:
return BarColor.PURPLE;
case GOLD:
case YELLOW:
return BarColor.YELLOW;
case LIGHT_PURPLE:
return BarColor.PINK;
case BLACK:
case WHITE:
case GRAY:
case DARK_GRAY:
default:
return BarColor.WHITE;
}
}
}
@@ -19,11 +19,9 @@
package de.steamwar.fightsystem.utils;
import de.steamwar.fightsystem.Config;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftEntity;
@@ -31,26 +29,12 @@ import org.bukkit.entity.Entity;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class CraftbukkitWrapper {
public static final CraftbukkitWrapper impl = new CraftbukkitWrapper();
protected net.minecraft.world.entity.Entity getEntity(Entity e) {
return ((CraftEntity) e).getHandle();
}
public float headRotation(Entity e) {
return getEntity(e).getYHeadRot();
}
public Stream<net.minecraft.world.entity.Entity> entityIterator() {
return StreamSupport.stream(((CraftWorld) Config.world).getHandle().getEntities().getAll().spliterator(), false);
}
public void setupGamerule() {
Config.world.setGameRule(GameRule.LOCATOR_BAR, false);
return ((CraftEntity) e).getHandle().getYHeadRot();
}
private LevelChunk getChunk(World world, int x, int z) {
@@ -34,12 +34,12 @@ import de.steamwar.fightsystem.winconditions.Wincondition;
import de.steamwar.linkage.Linked;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -142,6 +142,7 @@ public class FightUI {
BossBarType.RED_LEFT.text = leftRedText;
}
private final Map<Player, BossBar> barMap = new HashMap<>();
private void sendToPlayers() {
Bukkit.getOnlinePlayers().forEach(player -> {
BossBarType bar = BossBarType.byAngle(CraftbukkitWrapper.impl.headRotation(player));
@@ -154,7 +155,55 @@ public class FightUI {
}
}
BountifulWrapper.impl.sendBar(player, bar.team, bar.progress, FightSystem.getMessage().parse(bar.text.getMsg(), player, params));
String text = FightSystem.getMessage().parse(bar.text.getMsg(), player, params);
barMap.keySet().removeIf(p -> !p.isOnline());
if (!barMap.containsKey(player)) {
BossBar bar1 = Bukkit.createBossBar(player.getName(), BarColor.WHITE, BarStyle.SOLID);
barMap.put(player, bar1);
bar1.addPlayer(player);
}
BossBar bar1 = barMap.get(player);
BarColor color;
switch (bar.team.getColor()) {
case DARK_BLUE:
case DARK_AQUA:
case BLUE:
case AQUA:
color = BarColor.BLUE;
break;
case GREEN:
case DARK_GREEN:
color = BarColor.GREEN;
break;
case DARK_RED:
case RED:
color = BarColor.RED;
break;
case DARK_PURPLE:
color = BarColor.PURPLE;
break;
case GOLD:
case YELLOW:
color = BarColor.YELLOW;
break;
case LIGHT_PURPLE:
color = BarColor.PINK;
break;
case BLACK:
case WHITE:
case GRAY:
case DARK_GRAY:
default:
color = BarColor.WHITE;
break;
}
if (bar1.getColor() != color) bar1.setColor(color);
if (bar1.getProgress() != bar.progress) bar1.setProgress(bar.progress);
if (!bar1.getTitle().equals(text)) bar1.setTitle(text);
});
}
@@ -208,9 +257,17 @@ public class FightUI {
Bukkit.getOnlinePlayers().forEach(Player::resetTitle);
if (winner != null) {
Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_WIN", p, winner.getColor(), winner.getName()), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
Bukkit.getOnlinePlayers().forEach(p -> {
String title = FightSystem.getMessage().parse("UI_WIN", p, winner.getColor(), winner.getName());
String subtitle1 = FightSystem.getMessage().parse(subtitle, p, params);
p.sendTitle(title, subtitle1, 5, 40, 5);
});
} else {
Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, FightSystem.getMessage().parse("UI_DRAW", p), FightSystem.getMessage().parse(subtitle, p, params), 5, 40, 5));
Bukkit.getOnlinePlayers().forEach(p -> {
String title = FightSystem.getMessage().parse("UI_DRAW", p);
String subtitle1 = FightSystem.getMessage().parse(subtitle, p, params);
p.sendTitle(title, subtitle1, 5, 40, 5);
});
}
}
@@ -232,7 +289,10 @@ public class FightUI {
}
Message message = queue.poll();
Bukkit.getOnlinePlayers().forEach(p -> WorldOfColorWrapper.impl.sendTitle(p, " ", FightSystem.getMessage().parse(message.getMsg(), p, message.getParams()), 5, 40, 5));
Bukkit.getOnlinePlayers().forEach(p -> {
String subtitle = FightSystem.getMessage().parse(message.getMsg(), p, message.getParams());
p.sendTitle(" ", subtitle, 5, 40, 5);
});
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), FightUI::printSubtitle, 50);
subtitleScheduled = true;
}
@@ -1,97 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.block.data.type.Dispenser;
import org.bukkit.entity.Player;
import org.bukkit.entity.Pose;
import org.bukkit.event.block.BlockPhysicsEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockDataMeta;
import org.bukkit.inventory.meta.ItemMeta;
public class FlatteningWrapper {
public static final FlatteningWrapper impl = new FlatteningWrapper();
public boolean isWater(Block block) {
if (block.getType() == Material.WATER) return true;
BlockData data = block.getBlockData();
if (!(data instanceof Waterlogged)) return false;
return ((Waterlogged) data).isWaterlogged();
}
public boolean removeWater(Block block) {
Material type = block.getType();
if (type == Material.WATER || type == Material.LAVA) {
block.setType(Material.AIR);
return true;
}
BlockData data = block.getBlockData();
if (!(data instanceof Waterlogged)) return false;
Waterlogged waterlogged = (Waterlogged) data;
if (waterlogged.isWaterlogged()) {
block.setType(Material.AIR);
return true;
}
return false;
}
public boolean containsBlockMeta(ItemMeta meta) {
return meta instanceof BlockDataMeta && ((BlockDataMeta) meta).hasBlockData();
}
public boolean hasAttributeModifier(ItemStack stack) {
return stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers();
}
public boolean doRecord(BlockPhysicsEvent e) {
return e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR;
}
public void forceLoadChunk(World world, int cX, int cZ) {
world.setChunkForceLoaded(cX, cZ, true);
}
public boolean checkPistonMoving(Block block) {
return block.getType() == Material.MOVING_PISTON;
}
public boolean isFacingWater(Block dispenser) {
return dispenser.getRelative(((Dispenser) dispenser.getBlockData()).getFacing()).isLiquid();
}
public boolean isCrouching(Player player) {
return player.getPose() == Pose.SWIMMING;
}
public void sendBlockChange(Player player, Block block, Material type) {
player.sendBlockChange(block.getLocation(), type.createBlockData());
}
}
@@ -20,15 +20,25 @@
package de.steamwar.fightsystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.entity.REntity;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.FightTeam;
import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -91,7 +101,7 @@ public class Hull {
public void addPlayer(Player player) {
if (players.add(player)) {
for (Entity entity : entities) {
BlockIdWrapper.impl.untrackEntity(player, entity);
untrackEntity(player, entity);
}
}
}
@@ -99,7 +109,7 @@ public class Hull {
public void removePlayer(Player player, boolean activeRemoval) {
if (players.remove(player) && activeRemoval) {
for (Entity entity : entities) {
BlockIdWrapper.impl.trackEntity(player, entity);
trackEntity(player, entity);
}
// techhider triggers block change sending
}
@@ -110,18 +120,34 @@ public class Hull {
if (region.inRegion(location) && !visibility.get(new IntVector(location).toId(region))) {
if (entities.add(entity)) {
for (Player player : players) {
BlockIdWrapper.impl.untrackEntity(player, entity);
untrackEntity(player, entity);
}
}
} else {
if (entities.remove(entity)) {
for (Player player : players) {
BlockIdWrapper.impl.trackEntity(player, entity);
trackEntity(player, entity);
}
}
}
}
public void trackEntity(Player player, Entity entity) {
if (entity instanceof Player) {
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
}
player.showEntity(FightSystem.getPlugin(), entity);
}
public void untrackEntity(Player player, Entity entity) {
player.hideEntity(FightSystem.getPlugin(), entity);
if (entity instanceof Player) {
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
}
}
public void removeEntity(Entity entity) {
entities.remove(entity);
}
@@ -139,6 +165,15 @@ public class Hull {
rentities.remove(entity);
}
public void fill(boolean visible) {
visibility.set(0, visibility.size(), visible);
occluding.set(0, occluding.size(), !visible);
uncoveredSurface.clear();
for (BitSet directionalVisibility : visibilityDirections.values()) {
directionalVisibility.set(0, directionalVisibility.size(), visible);
}
}
public void initialize() {
visibility.clear();
occluding.clear();
@@ -189,7 +224,34 @@ public class Hull {
uncoveredSurface.clear();
for (Map.Entry<IntVector, List<IntVector>> entry : sectionWise.entrySet()) {
Object packet = HullHiderWrapper.impl.generateBlockChangePacket(entry.getValue());
Object result;
List<IntVector> changes = entry.getValue();
Object[] blockdata = new Object[changes.size()];
for (int i = 0; i < blockdata.length; i++) {
IntVector change = changes.get(i);
blockdata[i] = ((CraftBlockData) Config.world.getBlockData(change.getX(), change.getY(), change.getZ())).getState();
}
if (changes.size() > 1) {
IntVector section = changes.get(0);
section = new IntVector(section.getX() >> 4, section.getY() >> 4, section.getZ() >> 4);
int xOffset = 16 * section.getX();
int yOffset = 16 * section.getY();
int zOffset = 16 * section.getZ();
short[] pos = new short[changes.size()];
for (int i = 0; i < changes.size(); i++) {
IntVector change = changes.get(i);
pos[i] = (short) (((change.getX() - xOffset) << 8) + ((change.getZ() - zOffset) << 4) + (change.getY() - yOffset));
}
result = new ClientboundSectionBlocksUpdatePacket(SectionPos.of(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length));
} else {
IntVector pos = changes.get(0);
result = new ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]);
}
Object packet = result;
players.forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
}
}
@@ -19,8 +19,6 @@
package de.steamwar.fightsystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.entity.REntity;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.fight.Fight;
@@ -28,18 +26,9 @@ import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.Recording;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.techhider.TechHider;
import lombok.Getter;
import net.minecraft.core.Vec3i;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
@@ -55,17 +44,12 @@ import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
public class HullHider implements Listener {
@Getter
private final Map<FightTeam, Hull> hullMap = new HashMap<>();
private final Hull[] hulls;
private final Map<Class<?>, BiFunction<Player, Object, Object>> packetHiders = new HashMap<>();
private static final Class<?> packetPlayOutExplosion = ClientboundExplodePacket.class;
public HullHider() {
if (!TechHiderWrapper.ENABLED) {
@@ -76,34 +60,20 @@ public class HullHider implements Listener {
Fight.teams().forEach(team -> hullMap.put(team, new Hull(team)));
hulls = hullMap.values().toArray(new Hull[0]);
packetHiders.put(packetPlayOutWorldEvent, this::worldEventHider);
packetHiders.put(packetPlayOutExplosion, this::explosionHider);
posHiderGenerator(ClientboundLevelParticlesPacket.class, double.class, 1.0);
posHiderGenerator(ClientboundSoundPacket.class, int.class, 8.0);
new StateDependentListener(TechHiderWrapper.ENABLED, FightState.Schem, this);
new StateDependent(TechHiderWrapper.ENABLED, FightState.Schem) {
@Override
public void enable() {
packetHiders.forEach(TinyProtocol.instance::addFilter);
Bukkit.getOnlinePlayers().forEach(HullHider.this::updatePlayer);
}
@Override
public void disable() {
Bukkit.getOnlinePlayers().forEach(player -> removePlayer(player, true));
packetHiders.forEach(TinyProtocol.instance::removeFilter);
}
}.register();
new StateDependentTask(TechHiderWrapper.ENABLED, FightState.Schem, this::onTick, 0, 1);
}
public void initialize(FightTeam team) {
if (!TechHiderWrapper.ENABLED) return;
hullMap.get(team).initialize();
}
public void fill(FightTeam team, boolean visible) {
if (!TechHiderWrapper.ENABLED) return;
hullMap.get(team).fill(visible);
}
@EventHandler(priority = EventPriority.HIGH)
public void onJoin(PlayerJoinEvent e) {
@@ -138,7 +108,7 @@ public class HullHider implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockPhysic(BlockPhysicsEvent e) {
if (FlatteningWrapper.impl.doRecord(e)) {
if (e.getBlock() == e.getSourceBlock() || e.getChangedType() == Material.AIR) {
blockUpdate(e.getBlock(), e.getChangedType());
}
}
@@ -161,18 +131,6 @@ public class HullHider implements Listener {
return false;
}
public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
if (!TechHiderWrapper.ENABLED) return false;
for (Hull hull : hulls) {
if (hull.blockPrecise(player, chunkX, chunkY, chunkZ)) {
return true;
}
}
return false;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSpawn(EntitySpawnEvent e) {
for (Hull hull : hulls) {
@@ -210,39 +168,4 @@ public class HullHider implements Listener {
hull.removeREntity(e);
}
}
private static final Class<?> packetPlayOutWorldEvent = ClientboundLevelEventPacket.class;
private static final Reflection.Field<?> worldEventPosition = Reflection.getField(packetPlayOutWorldEvent, TechHider.blockPosition, 0);
public static final Reflection.Field<Integer> blockPositionY = Reflection.getField(Vec3i.class, int.class, 1);
private Object worldEventHider(Player player, Object packet) {
Object baseBlock = worldEventPosition.get(packet);
return packetHider(player, packet, new Location(Config.world, TechHider.blockPositionX.get(baseBlock), blockPositionY.get(baseBlock), TechHider.blockPositionZ.get(baseBlock)));
}
private Object explosionHider(Player player, Object packet) {
return ReflectionWrapper.impl.explosionHider(player, packet, this::packetHider);
}
private void posHiderGenerator(Class<?> type, Class<? extends Number> posType, double factor) {
Function<Object, Location> location = posPacketToLocation(type, posType, factor);
packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet)));
}
public static Function<Object, Location> posPacketToLocation(Class<?> type, Class<? extends Number> posType, double factor) {
Reflection.Field<? extends Number> x = Reflection.getField(type, posType, 0);
Reflection.Field<? extends Number> y = Reflection.getField(type, posType, 1);
Reflection.Field<? extends Number> z = Reflection.getField(type, posType, 2);
return packet -> new Location(Config.world, x.get(packet).doubleValue() / factor, y.get(packet).doubleValue() / factor, z.get(packet).doubleValue() / factor);
}
private Object packetHider(Player player, Object packet, Location location) {
for (Hull hull : hulls) {
if (hull.isLocationHidden(player, location)) return null;
}
return packet;
}
}
@@ -1,71 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import de.steamwar.fightsystem.Config;
import it.unimi.dsi.fastutil.shorts.Short2ObjectArrayMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import java.util.List;
public class HullHiderWrapper {
public static final HullHiderWrapper impl = new HullHiderWrapper();
public Object generateBlockChangePacket(List<Hull.IntVector> changes) {
Object[] blockdata = new Object[changes.size()];
for (int i = 0; i < blockdata.length; i++) {
Hull.IntVector change = changes.get(i);
blockdata[i] = ((CraftBlockData) Config.world.getBlockData(change.getX(), change.getY(), change.getZ())).getState();
}
return generateBlockChangePacket(changes, blockdata);
}
private Object generateBlockChangePacket(List<Hull.IntVector> changes, Object[] blockdata) {
if (changes.size() > 1) {
Hull.IntVector section = changes.get(0);
section = new Hull.IntVector(section.getX() >> 4, section.getY() >> 4, section.getZ() >> 4);
int xOffset = 16 * section.getX();
int yOffset = 16 * section.getY();
int zOffset = 16 * section.getZ();
short[] pos = new short[changes.size()];
for (int i = 0; i < changes.size(); i++) {
Hull.IntVector change = changes.get(i);
pos[i] = (short) (((change.getX() - xOffset) << 8) + ((change.getZ() - zOffset) << 4) + (change.getY() - yOffset));
}
return constructMultiBlockChange(section, pos, blockdata);
} else {
Hull.IntVector pos = changes.get(0);
return new ClientboundBlockUpdatePacket(new BlockPos(pos.getX(), pos.getY(), pos.getZ()), (BlockState) blockdata[0]);
}
}
protected Object constructMultiBlockChange(Hull.IntVector section, short[] pos, Object[] blockdata) {
return new ClientboundSectionBlocksUpdatePacket(SectionPos.of(section.getX(), section.getY(), section.getZ()), new Short2ObjectArrayMap<>(pos, blockdata, blockdata.length));
}
}
@@ -1,79 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import io.papermc.paper.datacomponent.DataComponentType;
import io.papermc.paper.datacomponent.DataComponentTypes;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashSet;
import java.util.Set;
public class ReflectionWrapper {
public static final ReflectionWrapper impl = new ReflectionWrapper();
private static final Set<DataComponentType> FORBIDDEN_TYPES = new HashSet<>();
static {
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_NAME);
FORBIDDEN_TYPES.add(DataComponentTypes.PROFILE);
FORBIDDEN_TYPES.add(DataComponentTypes.UNBREAKABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCK_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS);
FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS);
FORBIDDEN_TYPES.add(DataComponentTypes.TOOL);
FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON);
FORBIDDEN_TYPES.add(DataComponentTypes.FOOD);
FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON);
FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN);
FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES);
FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION);
FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST);
FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE);
}
public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) {
return packet;
}
public boolean hasItems(ItemStack stack) {
FORBIDDEN_TYPES.forEach(stack::resetData);
return false;
}
public interface PacketHiderFunction {
Object hide(Player player, Object packet, Location location);
}
}
@@ -21,7 +21,6 @@ package de.steamwar.fightsystem.utils;
import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.events.BoardingEvent;
import de.steamwar.fightsystem.events.TeamLeaveEvent;
import de.steamwar.fightsystem.events.TeamSpawnEvent;
@@ -30,56 +29,114 @@ import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.techhider.AccessPrivilegeProvider;
import de.steamwar.techhider.TechHider;
import lombok.Getter;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TechHiderWrapper extends StateDependent implements TechHider.LocationEvaluator, Listener {
public class TechHiderWrapper extends StateDependent implements Listener {
public static final boolean ENABLED = !Config.OnlyPublicSchematics && !Config.test() && Config.GameModeConfig.Techhider.Active;
@Getter
private final ConcurrentHashMap<Player, Region> hiddenRegion = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Player, Long> patterns = new ConcurrentHashMap<>();
private final TechHider techHider;
private final SecretKey key;
private final HullHider hullHider;
public TechHiderWrapper() {
super(ENABLED, FightState.Schem);
techHider = new TechHider(this, Config.GameModeConfig.Techhider.ObfuscateWith, Config.GameModeConfig.Techhider.HiddenBlocks, Config.GameModeConfig.Techhider.HiddenBlockEntities);
public TechHiderWrapper(HullHider hullHider) {
super(ENABLED, FightState.All);
try {
key = new SecretKeySpec(Files.readAllBytes(new File(System.getProperty("user.home"), "hullhider.key").toPath()), "AES");
} catch (IOException e) {
throw new SecurityException(e);
}
this.hullHider = hullHider;
new StateDependentListener(ENABLED, FightState.Schem, this);
new StateDependentListener(ENABLED, FightState.All, this);
register();
}
@Override
public void enable() {
techHider.enable();
Set<BlockState> blockStatesToObfuscate = getBlockStatesToHideFromMaterials(Config.GameModeConfig.Techhider.HiddenBlocks);
Set<Block> blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream()
.map(CraftMagicNumbers::getBlock)
.collect(Collectors.toUnmodifiableSet());
Set<BlockEntityType<?>> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream()
.map(id -> {
ResourceLocation loc = ResourceLocation.parse(id);
return BuiltInRegistries.BLOCK_ENTITY_TYPE.get(loc).orElse(null);
})
.filter(Objects::nonNull)
.map(Holder.Reference::value)
.collect(Collectors.toUnmodifiableSet());
new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() {
@Override
public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) {
return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ));
}
@Override
public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) {
return !hullHider.isBlockHidden(p, blockX, blockY, blockZ);
}
@Override
public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) {
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block);
}
@Override
public boolean isPlayerPrivilegedToAccessBlockState(Player p, int blockX, int blockY, int blockZ, BlockState blockState) {
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState);
}
// TODO will require entity tracking on the netty thread to prevent future race conditions
@Override
public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) {
net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId);
if (nmsEntity != null) {
return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ());
} else {
return true;
}
}
@Override
public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type) {
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type);
}
@Override
public boolean isPlayerPrivilegedToPerformAction(Player p) {
return p.getGameMode() != GameMode.SPECTATOR;
}
});
}
@Override
public void disable() {
techHider.disable();
hiddenRegion.clear();
}
@@ -118,72 +175,6 @@ public class TechHiderWrapper extends StateDependent implements TechHider.Locati
});
}
@Override
public boolean suppressInteractions(Player player) {
return player.getGameMode() == GameMode.SPECTATOR;
}
@Override
public boolean skipChunk(Player player, int chunkX, int chunkZ) {
return getHiddenRegion(player).chunkOutside(chunkX, chunkZ);
}
@Override
public boolean skipChunkSection(Player player, int chunkX, int chunkY, int chunkZ) {
return getHiddenRegion(player).chunkSectionOutside(chunkX, chunkY, chunkZ);
}
@Override
public TechHider.State check(Player player, int x, int y, int z) {
if (hiddenRegion.computeIfAbsent(player, this::getHiddenRegion).inRegion(x, y, z)) {
if (FightSystem.getHullHider().isBlockHidden(player, x, y, z)) {
int id = ((y & 3) << 4) + ((z & 3) << 2) + (x & 3);
return (patterns.computeIfAbsent(player, this::getPattern) & (1L << id)) == 0 ? TechHider.State.HIDE_AIR : TechHider.State.HIDE;
} else {
return TechHider.State.CHECK;
}
} else {
return TechHider.State.SKIP;
}
}
public long getPattern(Player player) {
long pattern = SteamwarUser.get(player.getUniqueId()).getId();
byte[] bytes = new byte[]{
(byte) (pattern & 0xFF),
(byte) ((pattern >>> 8) & 0xFF),
(byte) ((pattern >>> 16) & 0xFF),
(byte) ((pattern >>> 24) & 0xFF)
};
try {
Cipher cipher = Cipher.getInstance("AES_256/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
bytes = cipher.doFinal(bytes);
} catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException |
InvalidKeyException e) {
throw new SecurityException(e);
}
pattern = bytes[0] & 0xFFL |
((bytes[1] & 0xFFL) << 8) |
((bytes[2] & 0xFFL) << 16) |
((bytes[3] & 0xFFL) << 24);
/* XXXO OOOX
* XXXO OOOX
* XXOX OOXO
* OOXX XXOO */
pattern |= 0b1110_1110_1101_0011___0001_0001_0010_1100___0000_0000_0000_0000___0000_0000_0000_0000L;
return pattern;
}
@Override
public boolean blockPrecise(Player player, int chunkX, int chunkY, int chunkZ) {
return FightSystem.getHullHider().blockPrecise(player, chunkX, chunkY, chunkZ);
}
private Region getHiddenRegion(Player player) {
if (Config.isReferee(player)) return Region.EMPTY;
@@ -194,4 +185,27 @@ public class TechHiderWrapper extends StateDependent implements TechHider.Locati
return Fight.getOpposite(team).getExtendRegion();
}
private Stream<BlockState> getWaterloggedBlockStates() {
FluidState waterFluidState = Fluids.WATER.getSource(false);
return BuiltInRegistries.BLOCK.stream()
.map((block) -> block.getStateDefinition().getPossibleStates())
.flatMap(Collection::stream)
.filter((blockState -> blockState.getFluidState() == waterFluidState));
}
private Set<BlockState> getBlockStatesToHideFromMaterials(Set<Material> materials) {
Stream<BlockState> allStatesFromMaterials = materials.stream()
.map(CraftMagicNumbers::getBlock)
.map((block) -> block.getStateDefinition().getPossibleStates())
.flatMap(Collection::stream);
if(materials.contains(Material.WATER)) {
return Stream.concat(allStatesFromMaterials, getWaterloggedBlockStates()).collect(Collectors.toUnmodifiableSet());
}
else {
return allStatesFromMaterials.collect(Collectors.toUnmodifiableSet());
}
}
}
@@ -1,11 +0,0 @@
package de.steamwar.fightsystem.utils;
import net.minecraft.server.MinecraftServer;
public class TpsWarper {
public static final TpsWarper impl = new TpsWarper();
public void warp(float tps) {
MinecraftServer.getServer().tickRateManager().setTickRate(tps);
}
}
@@ -1,50 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.fightsystem.utils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.scoreboard.Team;
public class WorldOfColorWrapper {
public static final WorldOfColorWrapper impl = new WorldOfColorWrapper();
public void setTeamColor(Team team, ChatColor color) {
team.setColor(color);
}
public boolean isInBlock(Projectile e) {
if (e instanceof Arrow arrow) return arrow.isInBlock();
return false;
}
public void playSound(Location location, Sound sound, String soundCategory, float volume, float pitch) {
location.getWorld().playSound(location, sound, SoundCategory.valueOf(soundCategory), volume, pitch);
}
public void sendTitle(Player player, String title, String subtitle, int start, int hold, int stop) {
player.sendTitle(title, subtitle, start, hold, stop);
}
}
@@ -19,13 +19,22 @@
package de.steamwar.fightsystem.winconditions;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
@Linked
public class WinconditionWaterTechKO extends WinconditionBlocks {
public WinconditionWaterTechKO() {
super(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", FlatteningWrapper.impl::isWater);
super(Winconditions.WATER_TECH_KO, "WaterTechKO", "BAR_WATER", block -> {
if (block.getType() == Material.WATER) return true;
BlockData data = block.getBlockData();
if (!(data instanceof Waterlogged)) return false;
return ((Waterlogged) data).isWaterlogged();
});
}
}
+2 -31
View File
@@ -30,16 +30,6 @@ dependencies {
implementation(project(":FightSystem:FightSystem_Core"))
}
tasks.register<FightServer>("WarGear20") {
group = "run"
description = "Run a WarGear 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "WarGear20"
worldName = "arenas/Pentraki"
config = "WarGear20.yml"
}
tasks.register<FightServer>("HalloweenWS") {
group = "run"
description = "Run a Halloween 1.21 Fight Replay Server"
@@ -57,28 +47,9 @@ tasks.register<FightServer>("WarGear21") {
description = "Run a WarGear 1.21 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
dependsOn(":KotlinCore:shadowJar")
template = "WarGear21"
worldName = "arenas/Pentraki"
config = "WarGear20.yml"
config = "WarGear21.yml"
jar = "/jars/paper-1.21.6.jar"
}
tasks.register<FightServer>("SpaceCraftDev20") {
group = "run"
description = "Run a SpaceCraftDev 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "SpaceCraft20"
worldName = "arenas/AS_Horizon"
config = "SpaceCraftDev20.yml"
}
tasks.register<FightServer>("QuickGear20") {
group = "run"
description = "Run a QuickGear 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "QuickGear20"
worldName = "arenas/WarGearPark"
config = "QuickGear20.yml"
}
+9 -3
View File
@@ -19,6 +19,7 @@
plugins {
steamwar.java
widener
}
dependencies {
@@ -32,11 +33,16 @@ dependencies {
compileOnly(libs.fawe)
}
tasks.register<DevServer>("DevLobby20") {
widener {
fromCatalog(libs.nms)
fromCatalog(libs.paperapi)
}
tasks.register<DevServer>("DevLobby") {
group = "run"
description = "Run a 1.20 Dev Lobby"
description = "Run a Dev Lobby"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":LobbySystem:jar")
template = "Lobby20"
template = "Lobby21"
worldName = "Lobby"
}
@@ -20,7 +20,9 @@
package de.steamwar.lobby.boatrace;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RInteraction;
import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.util.LeaderboardManager;
import de.steamwar.sql.SteamwarUser;
@@ -58,11 +60,12 @@ public class BoatRace implements EventListener, Listener {
static {
boatNpcServer = new REntityServer();
REntity starter = new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC);
boatNpcServer.setCallback((player, rEntity, entityAction) -> {
if (rEntity != starter) return;
Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove);
if (entityAction == REntityServer.EntityAction.INTERACT && !oneNotStarted) {
new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC);
RInteraction interaction = new RInteraction(boatNpcServer, BoatRacePositions.NPC.clone().subtract(0.5, 0, 0.5));
interaction.setInteractionHeight(1.95f);
interaction.setCallback((player, entity, action) -> {
Bukkit.getWorlds().get(0).getEntities().stream().filter(e -> e.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove);
if (action == REntityAction.INTERACT && !oneNotStarted) {
oneNotStarted = true;
new BoatRace(player);
}
@@ -42,7 +42,7 @@ public class ColorInit {
if (inputStream == null) {
colors = new byte[256 * 256 * 256];
for (int i = 0; i < colors.length; i++) {
colors[i] = MapPalette.matchColor(new Color(i));
colors[i] = matchColor(new Color(i));
}
} else {
try {
@@ -57,4 +57,26 @@ public class ColorInit {
}
System.out.println("[ColorInit] Initialization took " + (System.currentTimeMillis() - time) + "ms");
}
public static byte matchColor(Color color) {
if (color.getAlpha() < 128) return 0;
if (MapPalette.mapColorCache != null && MapPalette.mapColorCache.isCached()) {
return MapPalette.mapColorCache.matchColor(color);
}
int index = 0;
double best = -1;
for (int i = 4; i < MapPalette.colors.length; i++) {
double distance = MapPalette.getDistance(color, MapPalette.colors[i]);
if (distance < best || best == -1) {
best = distance;
index = i;
}
}
// Minecraft has 248 colors, some of which have negative byte representations
return (byte) (index < 128 ? index : -129 + (index - 127));
}
}
@@ -254,7 +254,8 @@ public class CustomMap implements Listener {
int green = pixels[i2];
int i3 = (y * width + x) * numBands + 2;
int blue = pixels[i3];
Color nearest = MapPalette.getColor(ColorInit.getColorByte(red, green, blue));
int colorIndex = ColorInit.getColorByte(red, green, blue);
Color nearest = MapPalette.colors[colorIndex >= 0 ? colorIndex : colorIndex + 256];
pixels[(y * width + x) * numBands] = nearest.getRed();
pixels[i2] = nearest.getGreen();

Some files were not shown because too many files have changed in this diff Show More